From d428e791393399d85bfddf76f6a69814264d6a1c Mon Sep 17 00:00:00 2001 From: Jerry Zhang Date: Thu, 13 Jul 2017 18:17:35 -0700 Subject: Add libasyncio. libadbd_usb depends on libasyncio. Bug: 37916658 Test: Use adb in recovery on bullhead/marlin Change-Id: I77ac3bb2a580d29551e7b5c471bc3cfbf1a3219d --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index dc066dd48..c35dabc2d 100644 --- a/Android.mk +++ b/Android.mk @@ -127,6 +127,7 @@ LOCAL_STATIC_LIBRARIES := \ libmounts \ libz \ libminadbd \ + libasyncio \ libfusesideload \ libminui \ libpng \ -- cgit v1.2.3 From a48c494f138ddb99320583d00120674be1e3297c Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Wed, 19 Jul 2017 18:51:03 +0900 Subject: Build libminui with BOARD_VNDK_VERSION Use libdrm_platform which is a platform variant of libdrm. Bug: 63741047 Bug: 37342627 Test: BOARD_VNDK_VERSION=current m -j libminui Test: ryu recovery graphics test shows various graphics Change-Id: Ifd2c1432781a96538585cdf818aa728d628a2f5a --- minui/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minui/Android.mk b/minui/Android.mk index 4dfc65f8a..6522fcfd2 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -25,7 +25,7 @@ LOCAL_SRC_FILES := \ LOCAL_WHOLE_STATIC_LIBRARIES := \ libadf \ - libdrm \ + libdrm_platform \ libsync_recovery LOCAL_STATIC_LIBRARIES := \ -- cgit v1.2.3 From ded2dac082fd703f1cd7a5c3de59450cd3dc2530 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 21 Apr 2017 14:36:12 -0700 Subject: recovery: replace make_ext4 with e2fsprogs Execute mke2fs to create empty ext4 filesystem. Execute e2fsdroid to add files to filesystem. Test: enter recovery mode and wipe data Bug: 35219933 Change-Id: I10a9f4c1f4754ad864b2df45b1f879180ab33876 (cherry picked from commit ac31808cd37cfb98755e5821dbb2efb5fe5cb12a) --- Android.mk | 8 ++++--- roots.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++------ updater/install.cpp | 27 +++++++++++++++++++++-- 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/Android.mk b/Android.mk index dc066dd48..3eed7a696 100644 --- a/Android.mk +++ b/Android.mk @@ -85,9 +85,11 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_REQUIRED_MODULES := e2fsdroid_static mke2fs_static mke2fs.conf + ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) ifeq ($(HOST_OS),linux) -LOCAL_REQUIRED_MODULES := mkfs.f2fs +LOCAL_REQUIRED_MODULES += mkfs.f2fs endif endif @@ -120,6 +122,7 @@ LOCAL_STATIC_LIBRARIES := \ libverifier \ libbatterymonitor \ libbootloader_message \ + libfs_mgr \ libext4_utils \ libsparse \ libziparchive \ @@ -130,7 +133,6 @@ LOCAL_STATIC_LIBRARIES := \ libfusesideload \ libminui \ libpng \ - libfs_mgr \ libcrypto_utils \ libcrypto \ libvintf_recovery \ @@ -159,7 +161,7 @@ else endif ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),) -LOCAL_REQUIRED_MODULES := recovery-persist recovery-refresh +LOCAL_REQUIRED_MODULES += recovery-persist recovery-refresh endif include $(BUILD_EXECUTABLE) diff --git a/roots.cpp b/roots.cpp index 9b4270256..e98dfd448 100644 --- a/roots.cpp +++ b/roots.cpp @@ -27,7 +27,8 @@ #include #include -#include +#include +#include #include #include @@ -215,11 +216,60 @@ int format_volume(const char* volume, const char* directory) { } int result; if (strcmp(v->fs_type, "ext4") == 0) { - if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { - result = make_ext4fs_directory_align(v->blk_device, length, volume, sehandle, - directory, v->erase_blk_size, v->logical_blk_size); - } else { - result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory); + static constexpr int block_size = 4096; + int raid_stride = v->logical_blk_size / block_size; + int raid_stripe_width = v->erase_blk_size / block_size; + + // stride should be the max of 8kb and logical block size + if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) { + raid_stride = 8192 / block_size; + } + + const char* mke2fs_argv[] = { "/sbin/mke2fs_static", + "-F", + "-t", + "ext4", + "-b", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr }; + + int i = 5; + std::string block_size_str = std::to_string(block_size); + mke2fs_argv[i++] = block_size_str.c_str(); + + std::string ext_args; + if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { + ext_args = android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, + raid_stripe_width); + mke2fs_argv[i++] = "-E"; + mke2fs_argv[i++] = ext_args.c_str(); + } + + mke2fs_argv[i++] = v->blk_device; + + std::string size_str = std::to_string(length / block_size); + if (length != 0) { + mke2fs_argv[i++] = size_str.c_str(); + } + + result = exec_cmd(mke2fs_argv[0], const_cast(mke2fs_argv)); + if (result == 0 && directory != nullptr) { + const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", + "-e", + "-S", + "/file_contexts", + "-f", + directory, + "-a", + volume, + v->blk_device, + nullptr }; + + result = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); } } else { /* Has to be f2fs because we checked earlier. */ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { diff --git a/updater/install.cpp b/updater/install.cpp index ff79edce0..c9a3a0799 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -302,9 +302,32 @@ Value* FormatFn(const char* name, State* state, const std::vector(mke2fs_argv)); + if (status != 0) { + LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location + << ", falling back to make_ext4fs"; + status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle); + if (status != 0) { + LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location; + return StringValue(""); + } + return StringValue(location); + } + + const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-S", + "/file_contexts", "-a", mount_point.c_str(), + location.c_str(), nullptr }; + status = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); if (status != 0) { - LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location; + LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location; return StringValue(""); } return StringValue(location); -- cgit v1.2.3 From 6ed175d5412deeaec9691f85757e45452407b8e3 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 18 Jul 2017 11:29:40 -0700 Subject: Fix a case when brotli writer fails to write last few blocks of data receive_new_data may exit too early if the zip processor has sent all the raw data. As a result, the last few 'new' commands will fail even though the brotli decoder has more output in its buffer. Restruct the code so that 'NewThreadInfo' owns the decoder state solely; and receive_brotli_new_data is responsible for the decompression. Also reduce the test data size to 100 blocks to avoid the test timeout. Bug: 63802629 Test: recovery_component_test. on bullhead, apply full updates with and w/o brotli compressed entries, apply an incremental update. Change-Id: I9442f2536b74e48dbf7eeb062a8539c82c6dab47 --- tests/component/updater_test.cpp | 19 ++++- updater/blockimg.cpp | 175 ++++++++++++++++++--------------------- 2 files changed, 96 insertions(+), 98 deletions(-) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 357a39ef7..0a9328849 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -592,10 +592,10 @@ TEST_F(UpdaterTest, brotli_new_data) { ASSERT_EQ(0, zip_writer.StartEntry("new.dat.br", 0)); auto generator = []() { return rand() % 128; }; - // Generate 2048 blocks of random data. + // Generate 100 blocks of random data. std::string brotli_new_data; - brotli_new_data.reserve(4096 * 2048); - generate_n(back_inserter(brotli_new_data), 4096 * 2048, generator); + brotli_new_data.reserve(4096 * 100); + generate_n(back_inserter(brotli_new_data), 4096 * 100, generator); size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size()); std::vector encoded_data(encoded_size); @@ -609,8 +609,19 @@ TEST_F(UpdaterTest, brotli_new_data) { ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); ASSERT_EQ(0, zip_writer.FinishEntry()); + // Write a few small chunks of new data, then a large chunk, and finally a few small chunks. + // This helps us to catch potential short writes. std::vector transfer_list = { - "4", "2048", "0", "0", "new 4,0,512,512,1024", "new 2,1024,2048", + "4", + "100", + "0", + "0", + "new 2,0,1", + "new 2,1,2", + "new 4,2,50,50,97", + "new 2,97,98", + "new 2,98,99", + "new 2,99,100", }; ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); std::string commands = android::base::Join(transfer_list, '\n'); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 2bec487fe..a0b9ad233 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -158,20 +158,22 @@ class RangeSinkWriter { CHECK_NE(tgt.size(), static_cast(0)); }; - virtual ~RangeSinkWriter() {}; - bool Finished() const { return next_range_ == tgt_.size() && current_range_left_ == 0; } - // Return number of bytes consumed; and 0 indicates a writing failure. - virtual size_t Write(const uint8_t* data, size_t size) { + size_t AvailableSpace() const { + return tgt_.blocks() * BLOCKSIZE - bytes_written_; + } + + // Return number of bytes written; and 0 indicates a writing failure. + size_t Write(const uint8_t* data, size_t size) { if (Finished()) { LOG(ERROR) << "range sink write overrun; can't write " << size << " bytes"; return 0; } - size_t consumed = 0; + size_t written = 0; while (size > 0) { // Move to the next range as needed. if (!SeekToOutputRange()) { @@ -191,18 +193,18 @@ class RangeSinkWriter { size -= write_now; current_range_left_ -= write_now; - consumed += write_now; + written += write_now; } - bytes_written_ += consumed; - return consumed; + bytes_written_ += written; + return written; } size_t BytesWritten() const { return bytes_written_; } - protected: + private: // Set up the output cursor, move to next range if needed. bool SeekToOutputRange() { // We haven't finished the current range yet. @@ -241,75 +243,6 @@ class RangeSinkWriter { size_t bytes_written_; }; -class BrotliNewDataWriter : public RangeSinkWriter { - public: - BrotliNewDataWriter(int fd, const RangeSet& tgt, BrotliDecoderState* state) - : RangeSinkWriter(fd, tgt), state_(state) {} - - size_t Write(const uint8_t* data, size_t size) override { - if (Finished()) { - LOG(ERROR) << "Brotli new data write overrun; can't write " << size << " bytes"; - return 0; - } - CHECK(state_ != nullptr); - - size_t consumed = 0; - while (true) { - // Move to the next range as needed. - if (!SeekToOutputRange()) { - break; - } - - size_t available_in = size; - size_t write_now = std::min(32768, current_range_left_); - uint8_t buffer[write_now]; - - size_t available_out = write_now; - uint8_t* next_out = buffer; - - // The brotli decoder will update |data|, |available_in|, |next_out| and |available_out|. - BrotliDecoderResult result = BrotliDecoderDecompressStream( - state_, &available_in, &data, &available_out, &next_out, nullptr); - - // We don't have a way to recover from the decode error; report the failure. - if (result == BROTLI_DECODER_RESULT_ERROR) { - LOG(ERROR) << "Decompression failed with " - << BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state_)); - return 0; - } - - if (write_all(fd_, buffer, write_now - available_out) == -1) { - return 0; - } - - LOG(DEBUG) << "bytes written: " << write_now - available_out << ", bytes consumed " - << size - available_in << ", decoder status " << result; - - // Update the total bytes written to output by the current writer; this is different from the - // consumed input bytes. - bytes_written_ += write_now - available_out; - current_range_left_ -= (write_now - available_out); - consumed += (size - available_in); - - // Update the remaining size. The input data ptr is already updated by brotli decoder - // function. - size = available_in; - - // Continue if we have more output to write, or more input to consume. - if (result == BROTLI_DECODER_RESULT_SUCCESS || - (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && size == 0)) { - break; - } - } - - return consumed; - } - - private: - // Pointer to the decoder state. (initialized by PerformBlockImageUpdate) - BrotliDecoderState* state_; -}; - /** * All of the data for all the 'new' transfers is contained in one file in the update package, * concatenated together in the order in which transfers.list will need it. We want to stream it out @@ -354,16 +287,73 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { // At this point nti->writer is set, and we own it. The main thread is waiting for it to // disappear from nti. - size_t consumed = nti->writer->Write(data, size); + size_t write_now = std::min(size, nti->writer->AvailableSpace()); + if (nti->writer->Write(data, write_now) != write_now) { + LOG(ERROR) << "Failed to write " << write_now << " bytes."; + return false; + } + + data += write_now; + size -= write_now; + + if (nti->writer->Finished()) { + // We have written all the bytes desired by this writer. + + pthread_mutex_lock(&nti->mu); + nti->writer = nullptr; + pthread_cond_broadcast(&nti->cv); + pthread_mutex_unlock(&nti->mu); + } + } + + return true; +} + +static bool receive_brotli_new_data(const uint8_t* data, size_t size, void* cookie) { + NewThreadInfo* nti = static_cast(cookie); + + while (size > 0 || BrotliDecoderHasMoreOutput(nti->brotli_decoder_state)) { + // Wait for nti->writer to be non-null, indicating some of this data is wanted. + pthread_mutex_lock(&nti->mu); + while (nti->writer == nullptr) { + pthread_cond_wait(&nti->cv, &nti->mu); + } + pthread_mutex_unlock(&nti->mu); + + // At this point nti->writer is set, and we own it. The main thread is waiting for it to + // disappear from nti. + + size_t buffer_size = std::min(32768, nti->writer->AvailableSpace()); + if (buffer_size == 0) { + LOG(ERROR) << "No space left in output range"; + return false; + } + uint8_t buffer[buffer_size]; + size_t available_in = size; + size_t available_out = buffer_size; + uint8_t* next_out = buffer; + + // The brotli decoder will update |data|, |available_in|, |next_out| and |available_out|. + BrotliDecoderResult result = BrotliDecoderDecompressStream( + nti->brotli_decoder_state, &available_in, &data, &available_out, &next_out, nullptr); - // We encounter a fatal error if we fail to consume any input bytes. If this happens, abort the - // extraction. - if (consumed == 0) { - LOG(ERROR) << "Failed to process " << size << " input bytes."; + if (result == BROTLI_DECODER_RESULT_ERROR) { + LOG(ERROR) << "Decompression failed with " + << BrotliDecoderErrorString(BrotliDecoderGetErrorCode(nti->brotli_decoder_state)); return false; } - data += consumed; - size -= consumed; + + LOG(DEBUG) << "bytes to write: " << buffer_size - available_out << ", bytes consumed " + << size - available_in << ", decoder status " << result; + + size_t write_now = buffer_size - available_out; + if (nti->writer->Write(buffer, write_now) != write_now) { + LOG(ERROR) << "Failed to write " << write_now << " bytes."; + return false; + } + + // Update the remaining size. The input data ptr is already updated by brotli decoder function. + size = available_in; if (nti->writer->Finished()) { // We have written all the bytes desired by this writer. @@ -380,8 +370,11 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { static void* unzip_new_data(void* cookie) { NewThreadInfo* nti = static_cast(cookie); - ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti); - + if (nti->brotli_compressed) { + ProcessZipEntryContents(nti->za, &nti->entry, receive_brotli_new_data, nti); + } else { + ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti); + } pthread_mutex_lock(&nti->mu); nti->receiver_available = false; if (nti->writer != nullptr) { @@ -1240,12 +1233,7 @@ static int PerformCommandNew(CommandParameters& params) { LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data"; pthread_mutex_lock(¶ms.nti.mu); - if (params.nti.brotli_compressed) { - params.nti.writer = - std::make_unique(params.fd, tgt, params.nti.brotli_decoder_state); - } else { - params.nti.writer = std::make_unique(params.fd, tgt); - } + params.nti.writer = std::make_unique(params.fd, tgt); pthread_cond_broadcast(¶ms.nti.cv); while (params.nti.writer != nullptr) { @@ -1485,7 +1473,6 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, if (params.canwrite) { params.nti.za = za; params.nti.entry = new_entry; - // The entry is compressed by brotli if has a 'br' extension. params.nti.brotli_compressed = android::base::EndsWith(new_data_fn->data, ".br"); if (params.nti.brotli_compressed) { // Initialize brotli decoder state. -- cgit v1.2.3 From ec57903a7ec0bfe3c2f39dd6ee9cfc3de4ed20e6 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 21 Jul 2017 12:13:15 -0700 Subject: Avoid crashing recovery with unwritable /cache. When /cache is unwritable, recovery hits a crash loop. Because it passes nullptr to fileno(3) when writing back the locale file. This prevents user from recovering a device - it cannot boot far enough to recovery menu which allows wiping /cache. Bug: 63927337 Test: Corrupt /cache and boot into recovery on bullhead: 1. m -j recoveryimage 2. fastboot erase cache 3. fastboot boot $OUT/recovery.img 4. recovery menu shows up. Change-Id: I1407743f802049eb48add56a36298b665cb86139 --- recovery.cpp | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index e2d993e23..55b12d5dd 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -477,40 +477,38 @@ static void copy_logs() { sync(); } -// clear the recovery command and prepare to boot a (hopefully working) system, +// Clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read). This function is // idempotent: call it as many times as you like. static void finish_recovery() { - // Save the locale to cache, so if recovery is next started up - // without a --locale argument (eg, directly from the bootloader) - // it will use the last-known locale. - if (!locale.empty() && has_cache) { - LOG(INFO) << "Saving locale \"" << locale << "\""; - - FILE* fp = fopen_path(LOCALE_FILE, "we"); - if (!android::base::WriteStringToFd(locale, fileno(fp))) { - PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE; - } - check_and_fclose(fp, LOCALE_FILE); + // Save the locale to cache, so if recovery is next started up without a '--locale' argument + // (e.g., directly from the bootloader) it will use the last-known locale. + if (!locale.empty() && has_cache) { + LOG(INFO) << "Saving locale \"" << locale << "\""; + if (ensure_path_mounted(LOCALE_FILE) != 0) { + LOG(ERROR) << "Failed to mount " << LOCALE_FILE; + } else if (!android::base::WriteStringToFile(locale, LOCALE_FILE)) { + PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE; } + } - copy_logs(); + copy_logs(); - // Reset to normal system boot so recovery won't cycle indefinitely. - std::string err; - if (!clear_bootloader_message(&err)) { - LOG(ERROR) << "Failed to clear BCB message: " << err; - } + // Reset to normal system boot so recovery won't cycle indefinitely. + std::string err; + if (!clear_bootloader_message(&err)) { + LOG(ERROR) << "Failed to clear BCB message: " << err; + } - // Remove the command file, so recovery won't repeat indefinitely. - if (has_cache) { - if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) { - LOG(WARNING) << "Can't unlink " << COMMAND_FILE; - } - ensure_path_unmounted(CACHE_ROOT); + // Remove the command file, so recovery won't repeat indefinitely. + if (has_cache) { + if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) { + LOG(WARNING) << "Can't unlink " << COMMAND_FILE; } + ensure_path_unmounted(CACHE_ROOT); + } - sync(); // For good measure. + sync(); // For good measure. } struct saved_log_file { -- cgit v1.2.3 From 5a1dee01df3af346729b5791606b72d59b8e9815 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 21 Jul 2017 15:15:31 -0700 Subject: update_verifier: Handle legacy care_map.txt gracefully. update_verifier should be backward compatible to not reject legacy care_map.txt from old releases, which could otherwise fail to boot into the new release. For example, we've changed the care_map format between N and O. An O update_verifier would fail to work with an N care_map.txt - a) we have switched update_verifier to read from device mapper in O; b) the last few blocks that contain metadata can't be read via device mapper. This could be a result of sideloading an O OTA while the device having a pending N update. Bug: 63544345 Test: As follows on sailfish: 1. Flash the device with this CL; 2. Put a copy of N care_map.txt at /data/ota_package/. Restore the permissions properly ('cache' group); 3. `adb reboot bootloader`; 4. `fastboot set_active ` 5. Device boots up into home screen, with a warning in logcat that says it has skipped legacy care_map.txt. Change-Id: I6acc88c9e655a9245e6531f176fef7953953935f --- update_verifier/update_verifier.cpp | 71 ++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index d3a5185b8..b49011a12 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -175,40 +175,53 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return true; } +// Returns true to indicate a passing verification (or the error should be ignored); Otherwise +// returns false on fatal errors, where we should reject the current boot and trigger a fallback. +// Note that update_verifier should be backward compatible to not reject care_map.txt from old +// releases, which could otherwise fail to boot into the new release. For example, we've changed +// the care_map format between N and O. An O update_verifier would fail to work with N +// care_map.txt. This could be a result of sideloading an O OTA while the device having a pending N +// update. bool verify_image(const std::string& care_map_name) { - android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY))); - // If the device is flashed before the current boot, it may not have care_map.txt - // in /data/ota_package. To allow the device to continue booting in this situation, - // we should print a warning and skip the block verification. - if (care_map_fd.get() == -1) { - PLOG(WARNING) << "Failed to open " << care_map_name; - return true; - } - // Care map file has four lines (two lines if vendor partition is not present): - // First line has the block partition name (system/vendor). - // Second line holds all ranges of blocks to verify. - // The next two lines have the same format but for vendor partition. - std::string file_content; - if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) { - LOG(ERROR) << "Error reading care map contents to string."; - return false; - } + android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY))); + // If the device is flashed before the current boot, it may not have care_map.txt + // in /data/ota_package. To allow the device to continue booting in this situation, + // we should print a warning and skip the block verification. + if (care_map_fd.get() == -1) { + PLOG(WARNING) << "Failed to open " << care_map_name; + return true; + } + // Care map file has four lines (two lines if vendor partition is not present): + // First line has the block partition name (system/vendor). + // Second line holds all ranges of blocks to verify. + // The next two lines have the same format but for vendor partition. + std::string file_content; + if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) { + LOG(ERROR) << "Error reading care map contents to string."; + return false; + } - std::vector lines; - lines = android::base::Split(android::base::Trim(file_content), "\n"); - if (lines.size() != 2 && lines.size() != 4) { - LOG(ERROR) << "Invalid lines in care_map: found " << lines.size() - << " lines, expecting 2 or 4 lines."; - return false; - } + std::vector lines; + lines = android::base::Split(android::base::Trim(file_content), "\n"); + if (lines.size() != 2 && lines.size() != 4) { + LOG(ERROR) << "Invalid lines in care_map: found " << lines.size() + << " lines, expecting 2 or 4 lines."; + return false; + } - for (size_t i = 0; i < lines.size(); i += 2) { - if (!read_blocks(lines[i], lines[i+1])) { - return false; - } + for (size_t i = 0; i < lines.size(); i += 2) { + // We're seeing an N care_map.txt. Skip the verification since it's not compatible with O + // update_verifier (the last few metadata blocks can't be read via device mapper). + if (android::base::StartsWith(lines[i], "/dev/block/")) { + LOG(WARNING) << "Found legacy care_map.txt; skipped."; + return true; + } + if (!read_blocks(lines[i], lines[i+1])) { + return false; } + } - return true; + return true; } static int reboot_device() { -- cgit v1.2.3 From 338be53ed30fc79a34b7e6c80dde7b3cde0d9f84 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 11 Jul 2017 16:45:04 -0700 Subject: Remove the obsolete reference to /file_contexts. This file no longer exists: - /file_contexts has been split into plat_file_contexts and nonplat_file_contexts since commit b236eb6ca204cefcb926e19bd5682f9dcad4021d (system/sepolicy). - It was named /file_contexts.bin prior to the split. '-S file_contexts' is also no longer required by e2fsdroid, since commit 2fff6fb036cbbb6dedd7da3d208b312a9038a5ce (external/e2fsprogs). It will load the file contexts via libselinux. Test: Trigger the path by performing a data wipe for converting to FBE. Change-Id: I179939da409e5c0415ae0ea0bf5ddb23f9e6331e (cherry picked from commit 7af933b6a6fd687bd17710ef6fda0ad5483e4d6d) --- roots.cpp | 4 +--- updater/install.cpp | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/roots.cpp b/roots.cpp index e98dfd448..c4afd5de3 100644 --- a/roots.cpp +++ b/roots.cpp @@ -260,8 +260,6 @@ int format_volume(const char* volume, const char* directory) { if (result == 0 && directory != nullptr) { const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", - "-S", - "/file_contexts", "-f", directory, "-a", @@ -270,7 +268,7 @@ int format_volume(const char* volume, const char* directory) { nullptr }; result = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); - } + } } else { /* Has to be f2fs because we checked earlier. */ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { LOG(ERROR) << "format_volume: crypt footer + negative length (" << length diff --git a/updater/install.cpp b/updater/install.cpp index c9a3a0799..bfe91e7f9 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -322,8 +322,7 @@ Value* FormatFn(const char* name, State* state, const std::vector(e2fsdroid_argv)); if (status != 0) { -- cgit v1.2.3 From c319613e06f996aed32da3a1c3e93bf9b04ffa95 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 24 Jul 2017 09:22:39 -0700 Subject: tests: Add a test to cover legacy care_map.txt handling. This is to cover the code added by commit 5a1dee01df3af346729b5791606b72d59b8e9815, where an O update_verifier should not reject N care_map.txt. Bug: 63544345 Test: recovery_component_test passes on marlin. Change-Id: Ia944e16cba3cc635098b3ffd92842d725b570fec --- tests/component/update_verifier_test.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index 5fc7ef63f..b04e1185e 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -81,3 +81,16 @@ TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) { ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); ASSERT_FALSE(verify_image(temp_file.path)); } + +TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) { + // This test relies on dm-verity support. + if (!verity_supported) { + GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; + return; + } + + TemporaryFile temp_file; + std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0"; + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); + ASSERT_TRUE(verify_image(temp_file.path)); +} -- cgit v1.2.3 From bb10e58eb1d13a7b774e03ca50b2a668aa67ebc3 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sat, 22 Jul 2017 16:30:34 -0700 Subject: roots.cpp: Reformatting the file. Mostly cosmetic changes. Removed the use of errno, and added constness to a few pointers. format_volume() and exec_cmd() will be cleaned up in a separate CL. Test: mmma -j bootable/recovery Change-Id: Ia12ce25a91c0bdd0e319f6da02ce1dc8377f265d --- roots.cpp | 194 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 93 insertions(+), 101 deletions(-) diff --git a/roots.cpp b/roots.cpp index c4afd5de3..0360bde16 100644 --- a/roots.cpp +++ b/roots.cpp @@ -16,147 +16,139 @@ #include "roots.h" -#include +#include +#include #include #include #include #include #include #include -#include -#include #include #include #include +#include #include #include #include "common.h" #include "mounts.h" -#include "cryptfs.h" -static struct fstab *fstab = NULL; +static struct fstab* fstab = nullptr; -extern struct selabel_handle *sehandle; +extern struct selabel_handle* sehandle; -void load_volume_table() -{ - int i; - int ret; - - fstab = fs_mgr_read_fstab_default(); - if (!fstab) { - LOG(ERROR) << "failed to read default fstab"; - return; - } +void load_volume_table() { + fstab = fs_mgr_read_fstab_default(); + if (!fstab) { + LOG(ERROR) << "Failed to read default fstab"; + return; + } - ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk"); - if (ret < 0 ) { - LOG(ERROR) << "failed to add /tmp entry to fstab"; - fs_mgr_free_fstab(fstab); - fstab = NULL; - return; - } + int ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk"); + if (ret == -1) { + LOG(ERROR) << "Failed to add /tmp entry to fstab"; + fs_mgr_free_fstab(fstab); + fstab = nullptr; + return; + } - printf("recovery filesystem table\n"); - printf("=========================\n"); - for (i = 0; i < fstab->num_entries; ++i) { - Volume* v = &fstab->recs[i]; - printf(" %d %s %s %s %lld\n", i, v->mount_point, v->fs_type, - v->blk_device, v->length); - } - printf("\n"); + printf("recovery filesystem table\n"); + printf("=========================\n"); + for (int i = 0; i < fstab->num_entries; ++i) { + const Volume* v = &fstab->recs[i]; + printf(" %d %s %s %s %lld\n", i, v->mount_point, v->fs_type, v->blk_device, v->length); + } + printf("\n"); } Volume* volume_for_path(const char* path) { - return fs_mgr_get_entry_for_mount_point(fstab, path); + return fs_mgr_get_entry_for_mount_point(fstab, path); } // Mount the volume specified by path at the given mount_point. int ensure_path_mounted_at(const char* path, const char* mount_point) { - Volume* v = volume_for_path(path); - if (v == NULL) { - LOG(ERROR) << "unknown volume for path [" << path << "]"; - return -1; - } - if (strcmp(v->fs_type, "ramdisk") == 0) { - // the ramdisk is always mounted. - return 0; - } + Volume* v = volume_for_path(path); + if (v == nullptr) { + LOG(ERROR) << "unknown volume for path [" << path << "]"; + return -1; + } + if (strcmp(v->fs_type, "ramdisk") == 0) { + // The ramdisk is always mounted. + return 0; + } - if (!scan_mounted_volumes()) { - LOG(ERROR) << "failed to scan mounted volumes"; - return -1; - } + if (!scan_mounted_volumes()) { + LOG(ERROR) << "Failed to scan mounted volumes"; + return -1; + } - if (!mount_point) { - mount_point = v->mount_point; - } + if (!mount_point) { + mount_point = v->mount_point; + } - MountedVolume* mv = find_mounted_volume_by_mount_point(mount_point); - if (mv) { - // volume is already mounted - return 0; - } + const MountedVolume* mv = find_mounted_volume_by_mount_point(mount_point); + if (mv != nullptr) { + // Volume is already mounted. + return 0; + } - mkdir(mount_point, 0755); // in case it doesn't already exist - - if (strcmp(v->fs_type, "ext4") == 0 || - strcmp(v->fs_type, "squashfs") == 0 || - strcmp(v->fs_type, "vfat") == 0) { - int result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options); - if (result == -1 && fs_mgr_is_formattable(v)) { - LOG(ERROR) << "failed to mount " << mount_point << " (" << strerror(errno) - << ") , formatting....."; - bool crypt_footer = fs_mgr_is_encryptable(v) && !strcmp(v->key_loc, "footer"); - if (fs_mgr_do_format(v, crypt_footer) == 0) { - result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options); - } else { - PLOG(ERROR) << "failed to format " << mount_point; - return -1; - } - } + mkdir(mount_point, 0755); // in case it doesn't already exist + + if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "squashfs") == 0 || + strcmp(v->fs_type, "vfat") == 0) { + int result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options); + if (result == -1 && fs_mgr_is_formattable(v)) { + PLOG(ERROR) << "Failed to mount " << mount_point << "; formatting"; + bool crypt_footer = fs_mgr_is_encryptable(v) && !strcmp(v->key_loc, "footer"); + if (fs_mgr_do_format(v, crypt_footer) == 0) { + result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options); + } else { + PLOG(ERROR) << "Failed to format " << mount_point; + return -1; + } + } - if (result == -1) { - PLOG(ERROR) << "failed to mount " << mount_point; - return -1; - } - return 0; + if (result == -1) { + PLOG(ERROR) << "Failed to mount " << mount_point; + return -1; } + return 0; + } - LOG(ERROR) << "unknown fs_type \"" << v->fs_type << "\" for " << mount_point; - return -1; + LOG(ERROR) << "unknown fs_type \"" << v->fs_type << "\" for " << mount_point; + return -1; } int ensure_path_mounted(const char* path) { - // Mount at the default mount point. - return ensure_path_mounted_at(path, nullptr); + // Mount at the default mount point. + return ensure_path_mounted_at(path, nullptr); } int ensure_path_unmounted(const char* path) { - Volume* v = volume_for_path(path); - if (v == NULL) { - LOG(ERROR) << "unknown volume for path [" << path << "]"; - return -1; - } - if (strcmp(v->fs_type, "ramdisk") == 0) { - // the ramdisk is always mounted; you can't unmount it. - return -1; - } + const Volume* v = volume_for_path(path); + if (v == nullptr) { + LOG(ERROR) << "unknown volume for path [" << path << "]"; + return -1; + } + if (strcmp(v->fs_type, "ramdisk") == 0) { + // The ramdisk is always mounted; you can't unmount it. + return -1; + } - if (!scan_mounted_volumes()) { - LOG(ERROR) << "failed to scan mounted volumes"; - return -1; - } + if (!scan_mounted_volumes()) { + LOG(ERROR) << "Failed to scan mounted volumes"; + return -1; + } - MountedVolume* mv = find_mounted_volume_by_mount_point(v->mount_point); - if (mv == NULL) { - // volume is already unmounted - return 0; - } + MountedVolume* mv = find_mounted_volume_by_mount_point(v->mount_point); + if (mv == nullptr) { + // Volume is already unmounted. + return 0; + } - return unmount_mounted_volume(mv); + return unmount_mounted_volume(mv); } static int exec_cmd(const char* path, char* const argv[]) { @@ -304,7 +296,7 @@ int format_volume(const char* volume, const char* directory) { } int format_volume(const char* volume) { - return format_volume(volume, NULL); + return format_volume(volume, nullptr); } int setup_install_mounts() { @@ -322,12 +314,12 @@ int setup_install_mounts() { if (strcmp(v->mount_point, "/tmp") == 0 || strcmp(v->mount_point, "/cache") == 0) { if (ensure_path_mounted(v->mount_point) != 0) { - LOG(ERROR) << "failed to mount " << v->mount_point; + LOG(ERROR) << "Failed to mount " << v->mount_point; return -1; } } else { if (ensure_path_unmounted(v->mount_point) != 0) { - LOG(ERROR) << "failed to unmount " << v->mount_point; + LOG(ERROR) << "Failed to unmount " << v->mount_point; return -1; } } -- cgit v1.2.3 From 7934985e0cac4a3849418af3b8c9671f4d61078a Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 24 Jul 2017 20:30:29 -0700 Subject: otautil: Delete dirUnlinkHierarchy(). This function has become obsolete since we've removed file-based OTA support (it was needed by 'delete_recursive' edify function earlier). Test: mmma -j bootable/recovery Test: Code search shows no active user of the function. Change-Id: If6faaa759d4c849b79acba4e6adb82baadc89f7a --- otautil/DirUtil.cpp | 56 --------------------------------------------- otautil/DirUtil.h | 13 ----------- tests/unit/dirutil_test.cpp | 32 -------------------------- 3 files changed, 101 deletions(-) diff --git a/otautil/DirUtil.cpp b/otautil/DirUtil.cpp index e08e360c0..ad344dedf 100644 --- a/otautil/DirUtil.cpp +++ b/otautil/DirUtil.cpp @@ -160,59 +160,3 @@ dirCreateHierarchy(const char *path, int mode, } return 0; } - -int -dirUnlinkHierarchy(const char *path) -{ - struct stat st; - DIR *dir; - struct dirent *de; - int fail = 0; - - /* is it a file or directory? */ - if (lstat(path, &st) < 0) { - return -1; - } - - /* a file, so unlink it */ - if (!S_ISDIR(st.st_mode)) { - return unlink(path); - } - - /* a directory, so open handle */ - dir = opendir(path); - if (dir == NULL) { - return -1; - } - - /* recurse over components */ - errno = 0; - while ((de = readdir(dir)) != NULL) { - //TODO: don't blow the stack - char dn[PATH_MAX]; - if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { - continue; - } - snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); - if (dirUnlinkHierarchy(dn) < 0) { - fail = 1; - break; - } - errno = 0; - } - /* in case readdir or unlink_recursive failed */ - if (fail || errno < 0) { - int save = errno; - closedir(dir); - errno = save; - return -1; - } - - /* close directory handle */ - if (closedir(dir) < 0) { - return -1; - } - - /* delete target directory */ - return rmdir(path); -} diff --git a/otautil/DirUtil.h b/otautil/DirUtil.h index 85b83c387..beecc1081 100644 --- a/otautil/DirUtil.h +++ b/otautil/DirUtil.h @@ -17,13 +17,8 @@ #ifndef MINZIP_DIRUTIL_H_ #define MINZIP_DIRUTIL_H_ -#include #include -#ifdef __cplusplus -extern "C" { -#endif - struct selabel_handle; /* Like "mkdir -p", try to guarantee that all directories @@ -43,12 +38,4 @@ int dirCreateHierarchy(const char *path, int mode, const struct utimbuf *timestamp, bool stripFileName, struct selabel_handle* sehnd); -/* rm -rf - */ -int dirUnlinkHierarchy(const char *path); - -#ifdef __cplusplus -} -#endif - #endif // MINZIP_DIRUTIL_H_ diff --git a/tests/unit/dirutil_test.cpp b/tests/unit/dirutil_test.cpp index 5e2ae4fb5..e62032c68 100644 --- a/tests/unit/dirutil_test.cpp +++ b/tests/unit/dirutil_test.cpp @@ -116,35 +116,3 @@ TEST(DirUtilTest, create_mode_and_timestamp) { ASSERT_EQ(0, rmdir((prefix + "/a/b").c_str())); ASSERT_EQ(0, rmdir((prefix + "/a").c_str())); } - -TEST(DirUtilTest, unlink_invalid) { - // File doesn't exist. - ASSERT_EQ(-1, dirUnlinkHierarchy("doesntexist")); - - // Nonexistent directory. - TemporaryDir td; - std::string path(td.path); - ASSERT_EQ(-1, dirUnlinkHierarchy((path + "/a").c_str())); - ASSERT_EQ(ENOENT, errno); -} - -TEST(DirUtilTest, unlink_smoke) { - // Unlink a file. - TemporaryFile tf; - ASSERT_EQ(0, dirUnlinkHierarchy(tf.path)); - ASSERT_EQ(-1, access(tf.path, F_OK)); - - TemporaryDir td; - std::string path(td.path); - constexpr mode_t mode = 0700; - ASSERT_EQ(0, mkdir((path + "/a").c_str(), mode)); - ASSERT_EQ(0, mkdir((path + "/a/b").c_str(), mode)); - ASSERT_EQ(0, mkdir((path + "/a/b/c").c_str(), mode)); - ASSERT_EQ(0, mkdir((path + "/a/d").c_str(), mode)); - - // Remove "../a" recursively. - ASSERT_EQ(0, dirUnlinkHierarchy((path + "/a").c_str())); - - // Verify it's gone. - ASSERT_EQ(-1, access((path + "/a").c_str(), F_OK)); -} -- cgit v1.2.3 From 5902691764e041bfed8edbc66a72e0854d18dfda Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 25 Jul 2017 07:48:59 -0700 Subject: updater: Remove dead make_parents(). Its former callers in RenameFn() and SymlinkFn() have been removed in commit 63d786cf22cb44fe32e8b9c1f18b32da3c9d2e1b. Test: mmma -j bootable/recovery Change-Id: I26ed126202554fc5840811ec7ae162da70593213 --- updater/install.cpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/updater/install.cpp b/updater/install.cpp index bfe91e7f9..8e54c2e75 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -95,34 +95,6 @@ void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) { uiPrint(state, error_msg); } -static bool is_dir(const std::string& dirpath) { - struct stat st; - return stat(dirpath.c_str(), &st) == 0 && S_ISDIR(st.st_mode); -} - -// Create all parent directories of name, if necessary. -static bool make_parents(const std::string& name) { - size_t prev_end = 0; - while (prev_end < name.size()) { - size_t next_end = name.find('/', prev_end + 1); - if (next_end == std::string::npos) { - break; - } - std::string dir_path = name.substr(0, next_end); - if (!is_dir(dir_path)) { - int result = mkdir(dir_path.c_str(), 0700); - if (result != 0) { - PLOG(ERROR) << "failed to mkdir " << dir_path << " when make parents for " << name; - return false; - } - - LOG(INFO) << "created [" << dir_path << "]"; - } - prev_end = next_end; - } - return true; -} - // mount(fs_type, partition_type, location, mount_point) // mount(fs_type, partition_type, location, mount_point, mount_options) -- cgit v1.2.3 From f3ccad58ddb256150858df55a5e6fe5a906c5754 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 24 Jul 2017 10:34:35 -0700 Subject: recovery: handle security footer before passing size to mke2fs mke2fs doesn't take negative size as reserved size. If footer is specified, compute fs size to be (max partition size - reserved footer size) / block_size Bug: 23686092 Bug: 63968011 Change-Id: Iac4e143bd26a70cfc81eb52a399d687e19b1049c --- roots.cpp | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/roots.cpp b/roots.cpp index 0360bde16..46e5c1bc3 100644 --- a/roots.cpp +++ b/roots.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -165,6 +166,23 @@ static int exec_cmd(const char* path, char* const argv[]) { return WEXITSTATUS(status); } +static ssize_t get_file_size(int fd, uint64_t reserve_len) { + struct stat buf; + int ret = fstat(fd, &buf); + if (ret) return 0; + + ssize_t computed_size; + if (S_ISREG(buf.st_mode)) { + computed_size = buf.st_size - reserve_len; + } else if (S_ISBLK(buf.st_mode)) { + computed_size = get_block_device_size(fd) - reserve_len; + } else { + computed_size = 0; + } + + return computed_size; +} + int format_volume(const char* volume, const char* directory) { Volume* v = volume_for_path(volume); if (v == NULL) { @@ -204,7 +222,16 @@ int format_volume(const char* volume, const char* directory) { if (v->length != 0) { length = v->length; } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) { - length = -CRYPT_FOOTER_OFFSET; + android::base::unique_fd fd(open(v->blk_device, O_RDONLY)); + if (fd < 0) { + PLOG(ERROR) << "get_file_size: failed to open " << v->blk_device; + return -1; + } + length = get_file_size(fd.get(), CRYPT_FOOTER_OFFSET); + if (length <= 0) { + LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device; + return -1; + } } int result; if (strcmp(v->fs_type, "ext4") == 0) { @@ -262,16 +289,6 @@ int format_volume(const char* volume, const char* directory) { result = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); } } else { /* Has to be f2fs because we checked earlier. */ - if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { - LOG(ERROR) << "format_volume: crypt footer + negative length (" << length - << ") not supported on " << v->fs_type; - return -1; - } - if (length < 0) { - LOG(ERROR) << "format_volume: negative length (" << length - << ") not supported on " << v->fs_type; - return -1; - } char *num_sectors = nullptr; if (length >= 512 && asprintf(&num_sectors, "%zd", length / 512) <= 0) { LOG(ERROR) << "format_volume: failed to create " << v->fs_type -- cgit v1.2.3 From 7022f33ec8531c742f8f4701552d687233901495 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 25 Jul 2017 09:52:36 -0700 Subject: recovery: Fix the flickering when turning on text mode. When there's no command specified when booting into debuggable builds (such as using `adb reboot recovery`), we turn on the text mode (i.e. recovery menu) directly. This CL fixes the issue to avoid showing the background image in a flash while turning on the text mode. Bug: 63985334 Test: `fastboot boot $OUT/recovery.img` and it shows the recovery menu directly without the no command image in a flash. Change-Id: Id86bbe346ab76c8defc95e2b423e695a86774b09 --- recovery.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 55b12d5dd..8f3e9bdea 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1594,15 +1594,14 @@ int main(int argc, char **argv) { ui->Print("Rebooting automatically.\n"); } } else if (!just_exit) { - status = INSTALL_NONE; // No command specified - ui->SetBackground(RecoveryUI::NO_COMMAND); - - // http://b/17489952 - // If this is an eng or userdebug build, automatically turn on the - // text display if no command is specified. - if (is_ro_debuggable()) { - ui->ShowText(true); - } + // If this is an eng or userdebug build, automatically turn on the text display if no command + // is specified. Note that this should be called before setting the background to avoid + // flickering the background image. + if (is_ro_debuggable()) { + ui->ShowText(true); + } + status = INSTALL_NONE; // No command specified + ui->SetBackground(RecoveryUI::NO_COMMAND); } if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { -- cgit v1.2.3 From 5e5e334b3beacd56004310514bd565cd0a40d92a Mon Sep 17 00:00:00 2001 From: Lennart Wieboldt Date: Tue, 25 Jul 2017 21:48:39 +0200 Subject: Remove LOCAL_CLANG clang is the default compiler since Android nougat Change-Id: I930bba431dc49970cb4491ed5fcf44b5e00e97df Signed-off-by: Lennart Wieboldt --- edify/Android.mk | 2 -- minadbd/Android.mk | 2 -- otafault/Android.mk | 1 - uncrypt/Android.mk | 1 - 4 files changed, 6 deletions(-) diff --git a/edify/Android.mk b/edify/Android.mk index d8058c16f..ffd54c208 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -34,7 +34,6 @@ LOCAL_MODULE := edify_parser LOCAL_YACCFLAGS := -v LOCAL_CPPFLAGS += -Wno-unused-parameter LOCAL_CPPFLAGS += -Wno-deprecated-register -LOCAL_CLANG := true LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. LOCAL_STATIC_LIBRARIES += libbase @@ -51,7 +50,6 @@ LOCAL_CFLAGS := -Werror LOCAL_CPPFLAGS := -Wno-unused-parameter LOCAL_CPPFLAGS += -Wno-deprecated-register LOCAL_MODULE := libedify -LOCAL_CLANG := true LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. LOCAL_STATIC_LIBRARIES += libbase diff --git a/minadbd/Android.mk b/minadbd/Android.mk index de0b0c890..8d86fd653 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -15,7 +15,6 @@ LOCAL_SRC_FILES := \ minadbd.cpp \ minadbd_services.cpp \ -LOCAL_CLANG := true LOCAL_MODULE := libminadbd LOCAL_CFLAGS := $(minadbd_cflags) LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration @@ -27,7 +26,6 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) -LOCAL_CLANG := true LOCAL_MODULE := minadbd_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_SRC_FILES := fuse_adb_provider_test.cpp diff --git a/otafault/Android.mk b/otafault/Android.mk index ec4cdb365..7b5aab0b8 100644 --- a/otafault/Android.mk +++ b/otafault/Android.mk @@ -32,7 +32,6 @@ LOCAL_CFLAGS := \ LOCAL_SRC_FILES := config.cpp ota_io.cpp LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libotafault -LOCAL_CLANG := true LOCAL_C_INCLUDES := bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) LOCAL_WHOLE_STATIC_LIBRARIES := $(otafault_static_libs) diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 59084b0bb..cb60c721e 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -16,7 +16,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_CLANG := true LOCAL_SRC_FILES := uncrypt.cpp LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. LOCAL_MODULE := uncrypt -- cgit v1.2.3 From 3c00facb3da63fb56d9a4f658702b53b0615d1dc Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sat, 22 Jul 2017 16:46:54 -0700 Subject: roots.cpp: Clean up format_volume(). Test: m -j bootable/recovery Test: Wipe data/cache in recovery Change-Id: I11a4bf34015e903992ae2fb100d0b59a9fb75e03 --- roots.cpp | 250 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 121 insertions(+), 129 deletions(-) diff --git a/roots.cpp b/roots.cpp index 46e5c1bc3..fdcbfe844 100644 --- a/roots.cpp +++ b/roots.cpp @@ -25,6 +25,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -152,18 +156,26 @@ int ensure_path_unmounted(const char* path) { return unmount_mounted_volume(mv); } -static int exec_cmd(const char* path, char* const argv[]) { - int status; - pid_t child; - if ((child = vfork()) == 0) { - execv(path, argv); - _exit(EXIT_FAILURE); - } - waitpid(child, &status, 0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOG(ERROR) << path << " failed with status " << WEXITSTATUS(status); - } - return WEXITSTATUS(status); +static int exec_cmd(const std::vector& args) { + CHECK_NE(static_cast(0), args.size()); + + std::vector argv(args.size()); + std::transform(args.cbegin(), args.cend(), argv.begin(), + [](const std::string& arg) { return const_cast(arg.c_str()); }); + argv.push_back(nullptr); + + pid_t child; + if ((child = vfork()) == 0) { + execv(argv[0], argv.data()); + _exit(EXIT_FAILURE); + } + + int status; + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status); + } + return WEXITSTATUS(status); } static ssize_t get_file_size(int fd, uint64_t reserve_len) { @@ -184,132 +196,112 @@ static ssize_t get_file_size(int fd, uint64_t reserve_len) { } int format_volume(const char* volume, const char* directory) { - Volume* v = volume_for_path(volume); - if (v == NULL) { - LOG(ERROR) << "unknown volume \"" << volume << "\""; - return -1; + const Volume* v = volume_for_path(volume); + if (v == nullptr) { + LOG(ERROR) << "unknown volume \"" << volume << "\""; + return -1; + } + if (strcmp(v->fs_type, "ramdisk") == 0) { + LOG(ERROR) << "can't format_volume \"" << volume << "\""; + return -1; + } + if (strcmp(v->mount_point, volume) != 0) { + LOG(ERROR) << "can't give path \"" << volume << "\" to format_volume"; + return -1; + } + if (ensure_path_unmounted(volume) != 0) { + LOG(ERROR) << "format_volume: Failed to unmount \"" << v->mount_point << "\""; + return -1; + } + if (strcmp(v->fs_type, "ext4") != 0 && strcmp(v->fs_type, "f2fs") != 0) { + LOG(ERROR) << "format_volume: fs_type \"" << v->fs_type << "\" unsupported"; + return -1; + } + + // If there's a key_loc that looks like a path, it should be a block device for storing encryption + // metadata. Wipe it too. + if (v->key_loc != nullptr && v->key_loc[0] == '/') { + LOG(INFO) << "Wiping " << v->key_loc; + int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644); + if (fd == -1) { + PLOG(ERROR) << "format_volume: Failed to open " << v->key_loc; + return -1; } - if (strcmp(v->fs_type, "ramdisk") == 0) { - // you can't format the ramdisk. - LOG(ERROR) << "can't format_volume \"" << volume << "\""; - return -1; + wipe_block_device(fd, get_file_size(fd)); + close(fd); + } + + ssize_t length = 0; + if (v->length != 0) { + length = v->length; + } else if (v->key_loc != nullptr && strcmp(v->key_loc, "footer") == 0) { + android::base::unique_fd fd(open(v->blk_device, O_RDONLY)); + if (fd == -1) { + PLOG(ERROR) << "get_file_size: failed to open " << v->blk_device; + return -1; } - if (strcmp(v->mount_point, volume) != 0) { - LOG(ERROR) << "can't give path \"" << volume << "\" to format_volume"; - return -1; + length = get_file_size(fd.get(), CRYPT_FOOTER_OFFSET); + if (length <= 0) { + LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device; + return -1; } + } - if (ensure_path_unmounted(volume) != 0) { - LOG(ERROR) << "format_volume failed to unmount \"" << v->mount_point << "\""; - return -1; + if (strcmp(v->fs_type, "ext4") == 0) { + static constexpr int kBlockSize = 4096; + std::vector mke2fs_args = { + "/sbin/mke2fs_static", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize), + }; + + int raid_stride = v->logical_blk_size / kBlockSize; + int raid_stripe_width = v->erase_blk_size / kBlockSize; + // stride should be the max of 8KB and logical block size + if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) { + raid_stride = 8192 / kBlockSize; + } + if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { + mke2fs_args.push_back("-E"); + mke2fs_args.push_back( + android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, raid_stripe_width)); + } + mke2fs_args.push_back(v->blk_device); + if (length != 0) { + mke2fs_args.push_back(std::to_string(length / kBlockSize)); } - if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "f2fs") == 0) { - // if there's a key_loc that looks like a path, it should be a - // block device for storing encryption metadata. wipe it too. - if (v->key_loc != NULL && v->key_loc[0] == '/') { - LOG(INFO) << "wiping " << v->key_loc; - int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644); - if (fd < 0) { - LOG(ERROR) << "format_volume: failed to open " << v->key_loc; - return -1; - } - wipe_block_device(fd, get_file_size(fd)); - close(fd); - } - - ssize_t length = 0; - if (v->length != 0) { - length = v->length; - } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) { - android::base::unique_fd fd(open(v->blk_device, O_RDONLY)); - if (fd < 0) { - PLOG(ERROR) << "get_file_size: failed to open " << v->blk_device; - return -1; - } - length = get_file_size(fd.get(), CRYPT_FOOTER_OFFSET); - if (length <= 0) { - LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device; - return -1; - } - } - int result; - if (strcmp(v->fs_type, "ext4") == 0) { - static constexpr int block_size = 4096; - int raid_stride = v->logical_blk_size / block_size; - int raid_stripe_width = v->erase_blk_size / block_size; - - // stride should be the max of 8kb and logical block size - if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) { - raid_stride = 8192 / block_size; - } - - const char* mke2fs_argv[] = { "/sbin/mke2fs_static", - "-F", - "-t", - "ext4", - "-b", - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr }; - - int i = 5; - std::string block_size_str = std::to_string(block_size); - mke2fs_argv[i++] = block_size_str.c_str(); - - std::string ext_args; - if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { - ext_args = android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, - raid_stripe_width); - mke2fs_argv[i++] = "-E"; - mke2fs_argv[i++] = ext_args.c_str(); - } - - mke2fs_argv[i++] = v->blk_device; - - std::string size_str = std::to_string(length / block_size); - if (length != 0) { - mke2fs_argv[i++] = size_str.c_str(); - } - - result = exec_cmd(mke2fs_argv[0], const_cast(mke2fs_argv)); - if (result == 0 && directory != nullptr) { - const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", - "-e", - "-f", - directory, - "-a", - volume, - v->blk_device, - nullptr }; - - result = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); - } - } else { /* Has to be f2fs because we checked earlier. */ - char *num_sectors = nullptr; - if (length >= 512 && asprintf(&num_sectors, "%zd", length / 512) <= 0) { - LOG(ERROR) << "format_volume: failed to create " << v->fs_type - << " command for " << v->blk_device; - return -1; - } - const char *f2fs_path = "/sbin/mkfs.f2fs"; - const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, nullptr}; - - result = exec_cmd(f2fs_path, (char* const*)f2fs_argv); - free(num_sectors); - } - if (result != 0) { - PLOG(ERROR) << "format_volume: make " << v->fs_type << " failed on " << v->blk_device; - return -1; - } - return 0; + int result = exec_cmd(mke2fs_args); + if (result == 0 && directory != nullptr) { + std::vector e2fsdroid_args = { + "/sbin/e2fsdroid_static", + "-e", + "-f", + directory, + "-a", + volume, + v->blk_device, + }; + result = exec_cmd(e2fsdroid_args); } - LOG(ERROR) << "format_volume: fs_type \"" << v->fs_type << "\" unsupported"; + if (result != 0) { + PLOG(ERROR) << "format_volume: Failed to make ext4 on " << v->blk_device; + return -1; + } + return 0; + } + + // Has to be f2fs because we checked earlier. + std::vector f2fs_args = { "/sbin/mkfs.f2fs", "-t", "-d1", v->blk_device }; + if (length >= 512) { + f2fs_args.push_back(std::to_string(length / 512)); + } + + int result = exec_cmd(f2fs_args); + if (result != 0) { + PLOG(ERROR) << "format_volume: Failed to make f2fs on " << v->blk_device; return -1; + } + return 0; } int format_volume(const char* volume) { -- cgit v1.2.3 From ac3d1edca0ea91a192c3a9d52efbc439702e9cae Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 23 Jul 2017 00:01:02 -0700 Subject: otautil: Clean up dirCreateHierarchy(). - Changed to std::string based implementation (mostly moved from the former make_parents() in updater/install.cpp); - Removed the timestamp parameter, which is only neeed by file-based OTA; - Changed the type of mode from int to mode_t; - Renamed dirCreateHierarchy() to mkdir_recursively(). Test: recovery_unit_test passes. Test: No external user of dirCreateHierarchy() in code search. Change-Id: I71f8c4b29bab625513bbc3af6d0d1ecdc3a2719a --- otautil/DirUtil.cpp | 200 +++++++++++++++++--------------------------- otautil/DirUtil.h | 38 ++++----- recovery.cpp | 26 +++--- tests/unit/dirutil_test.cpp | 32 +++---- 4 files changed, 119 insertions(+), 177 deletions(-) diff --git a/otautil/DirUtil.cpp b/otautil/DirUtil.cpp index ad344dedf..fffc82219 100644 --- a/otautil/DirUtil.cpp +++ b/otautil/DirUtil.cpp @@ -16,147 +16,101 @@ #include "DirUtil.h" +#include +#include #include -#include -#include -#include #include +#include #include -#include -#include -#include #include #include #include -typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus; - -static DirStatus -getPathDirStatus(const char *path) -{ - struct stat st; - int err; +enum class DirStatus { DMISSING, DDIR, DILLEGAL }; - err = stat(path, &st); - if (err == 0) { - /* Something's there; make sure it's a directory. - */ - if (S_ISDIR(st.st_mode)) { - return DDIR; - } - errno = ENOTDIR; - return DILLEGAL; - } else if (errno != ENOENT) { - /* Something went wrong, or something in the path - * is bad. Can't do anything in this situation. - */ - return DILLEGAL; +static DirStatus dir_status(const std::string& path) { + struct stat sb; + if (stat(path.c_str(), &sb) == 0) { + // Something's there; make sure it's a directory. + if (S_ISDIR(sb.st_mode)) { + return DirStatus::DDIR; } - return DMISSING; + errno = ENOTDIR; + return DirStatus::DILLEGAL; + } else if (errno != ENOENT) { + // Something went wrong, or something in the path is bad. Can't do anything in this situation. + return DirStatus::DILLEGAL; + } + return DirStatus::DMISSING; } -int -dirCreateHierarchy(const char *path, int mode, - const struct utimbuf *timestamp, bool stripFileName, - struct selabel_handle *sehnd) -{ - DirStatus ds; - - /* Check for an empty string before we bother - * making any syscalls. - */ - if (path[0] == '\0') { - errno = ENOENT; - return -1; +int mkdir_recursively(const std::string& input_path, mode_t mode, bool strip_filename, + const selabel_handle* sehnd) { + // Check for an empty string before we bother making any syscalls. + if (input_path.empty()) { + errno = ENOENT; + return -1; + } + + // Allocate a path that we can modify; stick a slash on the end to make things easier. + std::string path = input_path; + if (strip_filename) { + // Strip everything after the last slash. + size_t pos = path.rfind('/'); + if (pos == std::string::npos) { + errno = ENOENT; + return -1; } - // Allocate a path that we can modify; stick a slash on - // the end to make things easier. - std::string cpath = path; - if (stripFileName) { - // Strip everything after the last slash. - size_t pos = cpath.rfind('/'); - if (pos == std::string::npos) { - errno = ENOENT; - return -1; - } - cpath.resize(pos + 1); - } else { - // Make sure that the path ends in a slash. - cpath.push_back('/'); + path.resize(pos + 1); + } else { + // Make sure that the path ends in a slash. + path.push_back('/'); + } + + // See if it already exists. + DirStatus ds = dir_status(path); + if (ds == DirStatus::DDIR) { + return 0; + } else if (ds == DirStatus::DILLEGAL) { + return -1; + } + + // Walk up the path from the root and make each level. + size_t prev_end = 0; + while (prev_end < path.size()) { + size_t next_end = path.find('/', prev_end + 1); + if (next_end == std::string::npos) { + break; } - - /* See if it already exists. - */ - ds = getPathDirStatus(cpath.c_str()); - if (ds == DDIR) { - return 0; - } else if (ds == DILLEGAL) { + std::string dir_path = path.substr(0, next_end); + // Check this part of the path and make a new directory if necessary. + switch (dir_status(dir_path)) { + case DirStatus::DILLEGAL: + // Could happen if some other process/thread is messing with the filesystem. return -1; - } - - /* Walk up the path from the root and make each level. - * If a directory already exists, no big deal. - */ - const char *path_start = &cpath[0]; - char *p = &cpath[0]; - while (*p != '\0') { - /* Skip any slashes, watching out for the end of the string. - */ - while (*p != '\0' && *p == '/') { - p++; - } - if (*p == '\0') { - break; + case DirStatus::DMISSING: { + char* secontext = nullptr; + if (sehnd) { + selabel_lookup(const_cast(sehnd), &secontext, dir_path.c_str(), mode); + setfscreatecon(secontext); } - - /* Find the end of the next path component. - * We know that we'll see a slash before the NUL, - * because we added it, above. - */ - while (*p != '/') { - p++; + int err = mkdir(dir_path.c_str(), mode); + if (secontext) { + freecon(secontext); + setfscreatecon(nullptr); } - *p = '\0'; - - /* Check this part of the path and make a new directory - * if necessary. - */ - ds = getPathDirStatus(path_start); - if (ds == DILLEGAL) { - /* Could happen if some other process/thread is - * messing with the filesystem. - */ - return -1; - } else if (ds == DMISSING) { - int err; - - char *secontext = NULL; - - if (sehnd) { - selabel_lookup(sehnd, &secontext, path_start, mode); - setfscreatecon(secontext); - } - - err = mkdir(path_start, mode); - - if (secontext) { - freecon(secontext); - setfscreatecon(NULL); - } - - if (err != 0) { - return -1; - } - if (timestamp != NULL && utime(path_start, timestamp)) { - return -1; - } + if (err != 0) { + return -1; } - // else, this directory already exists. - - // Repair the path and continue. - *p = '/'; + break; + } + default: + // Already exists. + break; } - return 0; + prev_end = next_end; + } + return 0; } diff --git a/otautil/DirUtil.h b/otautil/DirUtil.h index beecc1081..85d6c16d1 100644 --- a/otautil/DirUtil.h +++ b/otautil/DirUtil.h @@ -14,28 +14,26 @@ * limitations under the License. */ -#ifndef MINZIP_DIRUTIL_H_ -#define MINZIP_DIRUTIL_H_ +#ifndef OTAUTIL_DIRUTIL_H_ +#define OTAUTIL_DIRUTIL_H_ -#include +#include // mode_t + +#include struct selabel_handle; -/* Like "mkdir -p", try to guarantee that all directories - * specified in path are present, creating as many directories - * as necessary. The specified mode is passed to all mkdir - * calls; no modifications are made to umask. - * - * If stripFileName is set, everything after the final '/' - * is stripped before creating the directory hierarchy. - * - * If timestamp is non-NULL, new directories will be timestamped accordingly. - * - * Returns 0 on success; returns -1 (and sets errno) on failure - * (usually if some element of path is not a directory). - */ -int dirCreateHierarchy(const char *path, int mode, - const struct utimbuf *timestamp, bool stripFileName, - struct selabel_handle* sehnd); +// Like "mkdir -p", try to guarantee that all directories specified in path are present, creating as +// many directories as necessary. The specified mode is passed to all mkdir calls; no modifications +// are made to umask. +// +// If strip_filename is set, everything after the final '/' is stripped before creating the +// directory +// hierarchy. +// +// Returns 0 on success; returns -1 (and sets errno) on failure (usually if some element of path is +// not a directory). +int mkdir_recursively(const std::string& path, mode_t mode, bool strip_filename, + const struct selabel_handle* sehnd); -#endif // MINZIP_DIRUTIL_H_ +#endif // OTAUTIL_DIRUTIL_H_ diff --git a/recovery.cpp b/recovery.cpp index 8f3e9bdea..233e56246 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -178,19 +178,19 @@ struct selabel_handle* sehandle; * 7b. the user reboots (pulling the battery, etc) into the main system */ -// open a given path, mounting partitions as necessary -FILE* fopen_path(const char *path, const char *mode) { - if (ensure_path_mounted(path) != 0) { - LOG(ERROR) << "Can't mount " << path; - return NULL; - } - - // 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])) dirCreateHierarchy(path, 0777, NULL, 1, sehandle); +// Open a given path, mounting partitions as necessary. +FILE* fopen_path(const char* path, const char* mode) { + if (ensure_path_mounted(path) != 0) { + LOG(ERROR) << "Can't mount " << path; + return nullptr; + } - FILE *fp = fopen(path, mode); - return fp; + // 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, mode); } // close a file, log an error if the error indicator is set @@ -593,7 +593,7 @@ static bool erase_volume(const char* volume) { if (is_cache) { // Re-create the log dir and write back the log entries. if (ensure_path_mounted(CACHE_LOG_DIR) == 0 && - dirCreateHierarchy(CACHE_LOG_DIR, 0777, nullptr, false, sehandle) == 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)) { diff --git a/tests/unit/dirutil_test.cpp b/tests/unit/dirutil_test.cpp index e62032c68..7f85d13ea 100644 --- a/tests/unit/dirutil_test.cpp +++ b/tests/unit/dirutil_test.cpp @@ -26,23 +26,23 @@ TEST(DirUtilTest, create_invalid) { // Requesting to create an empty dir is invalid. - ASSERT_EQ(-1, dirCreateHierarchy("", 0755, nullptr, false, nullptr)); + ASSERT_EQ(-1, mkdir_recursively("", 0755, false, nullptr)); ASSERT_EQ(ENOENT, errno); // Requesting to strip the name with no slash present. - ASSERT_EQ(-1, dirCreateHierarchy("abc", 0755, nullptr, true, nullptr)); + ASSERT_EQ(-1, mkdir_recursively("abc", 0755, true, nullptr)); ASSERT_EQ(ENOENT, errno); // Creating a dir that already exists. TemporaryDir td; - ASSERT_EQ(0, dirCreateHierarchy(td.path, 0755, nullptr, false, nullptr)); + ASSERT_EQ(0, mkdir_recursively(td.path, 0755, false, nullptr)); // "///" is a valid dir. - ASSERT_EQ(0, dirCreateHierarchy("///", 0755, nullptr, false, nullptr)); + ASSERT_EQ(0, mkdir_recursively("///", 0755, false, nullptr)); // Request to create a dir, but a file with the same name already exists. TemporaryFile tf; - ASSERT_EQ(-1, dirCreateHierarchy(tf.path, 0755, nullptr, false, nullptr)); + ASSERT_EQ(-1, mkdir_recursively(tf.path, 0755, false, nullptr)); ASSERT_EQ(ENOTDIR, errno); } @@ -51,7 +51,7 @@ TEST(DirUtilTest, create_smoke) { std::string prefix(td.path); std::string path = prefix + "/a/b"; constexpr mode_t mode = 0755; - ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), mode, nullptr, false, nullptr)); + ASSERT_EQ(0, mkdir_recursively(path, mode, false, nullptr)); // Verify. struct stat sb; @@ -69,7 +69,7 @@ TEST(DirUtilTest, create_strip_filename) { TemporaryDir td; std::string prefix(td.path); std::string path = prefix + "/a/b"; - ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), 0755, nullptr, true, nullptr)); + ASSERT_EQ(0, mkdir_recursively(path, 0755, true, nullptr)); // Verify that "../a" exists but not "../a/b". struct stat sb; @@ -83,31 +83,21 @@ TEST(DirUtilTest, create_strip_filename) { ASSERT_EQ(0, rmdir((prefix + "/a").c_str())); } -TEST(DirUtilTest, create_mode_and_timestamp) { +TEST(DirUtilTest, create_mode) { TemporaryDir td; std::string prefix(td.path); std::string path = prefix + "/a/b"; - // Set the timestamp to 8/1/2008. - constexpr struct utimbuf timestamp = { 1217592000, 1217592000 }; constexpr mode_t mode = 0751; - ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), mode, ×tamp, false, nullptr)); + ASSERT_EQ(0, mkdir_recursively(path, mode, false, nullptr)); - // Verify the mode and timestamp for "../a/b". + // Verify the mode for "../a/b". struct stat sb; ASSERT_EQ(0, stat(path.c_str(), &sb)) << strerror(errno); ASSERT_TRUE(S_ISDIR(sb.st_mode)); constexpr mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO; ASSERT_EQ(mode, sb.st_mode & mask); - timespec time; - time.tv_sec = 1217592000; - time.tv_nsec = 0; - - ASSERT_EQ(time.tv_sec, static_cast(sb.st_atime)); - ASSERT_EQ(time.tv_sec, static_cast(sb.st_mtime)); - - // Verify the mode for "../a". Note that the timestamp for intermediate directories (e.g. "../a") - // may not be 'timestamp' according to the current implementation. + // Verify the mode for "../a". ASSERT_EQ(0, stat((prefix + "/a").c_str(), &sb)) << strerror(errno); ASSERT_TRUE(S_ISDIR(sb.st_mode)); ASSERT_EQ(mode, sb.st_mode & mask); -- cgit v1.2.3 From 8b8e23d5cc38d4f9139b3b3cdca4a9c5ec51139e Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 27 Jul 2017 11:42:17 -0700 Subject: Add more specific error codes for uncrypt failures Add the error codes when we fail to find the realpath, or fail to find the block_device. Bug: 63737759 Test: mma Change-Id: Icf15368ad3e7345c747d9083da2f049cc8acd571 --- error_code.h | 2 ++ uncrypt/uncrypt.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/error_code.h b/error_code.h index 9fe047c91..4cbad4cfe 100644 --- a/error_code.h +++ b/error_code.h @@ -68,6 +68,8 @@ enum UncryptErrorCode { kUncryptFileCloseError, kUncryptFileRenameError, kUncryptPackageMissingError, + kUncryptRealpathFindError, + kUncryptBlockDeviceFindError, }; #endif // _ERROR_CODE_H_ diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 07d183be2..35998bcba 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -448,20 +448,20 @@ static int produce_block_map(const char* path, const char* map_file, const char* static int uncrypt(const char* input_path, const char* map_file, const int socket) { LOG(INFO) << "update package is \"" << input_path << "\""; - // Turn the name of the file we're supposed to convert into an - // absolute path, so we can find what filesystem it's on. + // Turn the name of the file we're supposed to convert into an absolute path, so we can find + // what filesystem it's on. char path[PATH_MAX+1]; - if (realpath(input_path, path) == NULL) { + if (realpath(input_path, path) == nullptr) { PLOG(ERROR) << "failed to convert \"" << input_path << "\" to absolute path"; - return 1; + return kUncryptRealpathFindError; } bool encryptable; bool encrypted; const char* blk_dev = find_block_device(path, &encryptable, &encrypted); - if (blk_dev == NULL) { + if (blk_dev == nullptr) { LOG(ERROR) << "failed to find block device for " << path; - return 1; + return kUncryptBlockDeviceFindError; } // If the filesystem it's on isn't encrypted, we only produce the -- cgit v1.2.3 From b9e7fc7fa9c70df36881b2166e8bd2bcfa87fe2e Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 26 Jul 2017 16:41:24 -0700 Subject: Add implemention of SortedRangeSet This is useful in imgdiff to maintain the block ranges of splitted source image. Bug: 34220646 Test: mma && unit tests pass Change-Id: I6427f2ea50f0e3b0aa3dd01880ec0206679b7429 --- tests/unit/rangeset_test.cpp | 47 +++++++++++++++ updater/include/updater/rangeset.h | 116 ++++++++++++++++++++++++++++++++++++- 2 files changed, 162 insertions(+), 1 deletion(-) diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp index 3c6d77ef5..3993cb9ea 100644 --- a/tests/unit/rangeset_test.cpp +++ b/tests/unit/rangeset_test.cpp @@ -110,3 +110,50 @@ TEST(RangeSetTest, iterators) { } ASSERT_EQ((std::vector{ Range{ 8, 10 }, Range{ 1, 5 } }), ranges); } + +TEST(RangeSetTest, tostring) { + ASSERT_EQ("2,1,6", RangeSet::Parse("2,1,6").ToString()); + ASSERT_EQ("4,1,5,8,10", RangeSet::Parse("4,1,5,8,10").ToString()); + ASSERT_EQ("6,1,3,4,6,15,22", RangeSet::Parse("6,1,3,4,6,15,22").ToString()); +} + +TEST(SortedRangeSetTest, insertion) { + SortedRangeSet rs({ { 2, 3 }, { 4, 6 }, { 8, 14 } }); + rs.Insert({ 1, 2 }); + ASSERT_EQ(SortedRangeSet({ { 1, 3 }, { 4, 6 }, { 8, 14 } }), rs); + ASSERT_EQ(static_cast(10), rs.blocks()); + rs.Insert({ 3, 5 }); + ASSERT_EQ(SortedRangeSet({ { 1, 6 }, { 8, 14 } }), rs); + ASSERT_EQ(static_cast(11), rs.blocks()); + + SortedRangeSet r1({ { 20, 22 }, { 15, 18 } }); + rs.Insert(r1); + ASSERT_EQ(SortedRangeSet({ { 1, 6 }, { 8, 14 }, { 15, 18 }, { 20, 22 } }), rs); + ASSERT_EQ(static_cast(16), rs.blocks()); + + SortedRangeSet r2({ { 2, 7 }, { 15, 21 }, { 20, 25 } }); + rs.Insert(r2); + ASSERT_EQ(SortedRangeSet({ { 1, 7 }, { 8, 14 }, { 15, 25 } }), rs); + ASSERT_EQ(static_cast(22), rs.blocks()); +} + +TEST(SortedRangeSetTest, file_range) { + SortedRangeSet rs; + rs.Insert(4096, 4096); + ASSERT_EQ(SortedRangeSet({ { 1, 2 } }), rs); + // insert block 2-9 + rs.Insert(4096 * 3 - 1, 4096 * 7); + ASSERT_EQ(SortedRangeSet({ { 1, 10 } }), rs); + // insert block 15-19 + rs.Insert(4096 * 15 + 1, 4096 * 4); + ASSERT_EQ(SortedRangeSet({ { 1, 10 }, { 15, 20 } }), rs); + + // rs overlaps block 2-2 + ASSERT_TRUE(rs.Overlaps(4096 * 2 - 1, 10)); + ASSERT_FALSE(rs.Overlaps(4096 * 10, 4096 * 5)); + + ASSERT_EQ(static_cast(10), rs.GetOffsetInRangeSet(4106)); + ASSERT_EQ(static_cast(40970), rs.GetOffsetInRangeSet(4096 * 16 + 10)); + // block#10 not in range. + ASSERT_EXIT(rs.GetOffsetInRangeSet(40970), ::testing::KilledBySignal(SIGABRT), ""); +} \ No newline at end of file diff --git a/updater/include/updater/rangeset.h b/updater/include/updater/rangeset.h index fad038043..b67c98724 100644 --- a/updater/include/updater/rangeset.h +++ b/updater/include/updater/rangeset.h @@ -24,6 +24,7 @@ #include #include +#include #include using Range = std::pair; @@ -74,6 +75,18 @@ class RangeSet { return RangeSet(std::move(pairs)); } + std::string ToString() const { + if (ranges_.empty()) { + return ""; + } + std::string result = std::to_string(ranges_.size() * 2); + for (const auto& r : ranges_) { + result += android::base::StringPrintf(",%zu,%zu", r.first, r.second); + } + + return result; + } + // Get the block number for the i-th (starting from 0) block in the RangeSet. size_t GetBlockNumber(size_t idx) const { CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")"; @@ -157,8 +170,109 @@ class RangeSet { return ranges_ != other.ranges_; } - private: + protected: // Actual limit for each value and the total number are both INT_MAX. std::vector ranges_; size_t blocks_; }; + +static constexpr size_t kBlockSize = 4096; + +// The class is a sorted version of a RangeSet; and it's useful in imgdiff to split the input +// files when we're handling large zip files. Specifically, we can treat the input file as a +// continuous RangeSet (i.e. RangeSet("0-99") for a 100 blocks file); and break it down into +// several smaller chunks based on the zip entries. + +// For example, [source: 0-99] can be split into +// [split_src1: 10-29]; [split_src2: 40-49, 60-69]; [split_src3: 70-89] +// Here "10-29" simply means block 10th to block 29th with respect to the original input file. +// Also, note that the split sources should be mutual exclusive, but they don't need to cover +// every block in the original source. +class SortedRangeSet : public RangeSet { + public: + SortedRangeSet() {} + + // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. + explicit SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { + std::sort(ranges_.begin(), ranges_.end()); + } + + void Insert(const Range& to_insert) { + SortedRangeSet rs({ to_insert }); + Insert(rs); + } + + // Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges. + void Insert(const SortedRangeSet& rs) { + if (rs.size() == 0) { + return; + } + // Merge and sort the two RangeSets. + std::vector temp = std::move(ranges_); + std::copy(rs.begin(), rs.end(), std::back_inserter(temp)); + std::sort(temp.begin(), temp.end()); + + Clear(); + // Trim overlaps and insert the result back to ranges_. + Range to_insert = temp.front(); + for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) { + if (it->first <= to_insert.second) { + to_insert.second = std::max(to_insert.second, it->second); + } else { + ranges_.push_back(to_insert); + blocks_ += (to_insert.second - to_insert.first); + to_insert = *it; + } + } + ranges_.push_back(to_insert); + blocks_ += (to_insert.second - to_insert.first); + } + + void Clear() { + blocks_ = 0; + ranges_.clear(); + } + + using RangeSet::Overlaps; + bool Overlaps(size_t start, size_t len) const { + RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } }); + return Overlaps(rs); + } + + // Compute the block range the file occupies, and insert that range. + void Insert(size_t start, size_t len) { + Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 }; + Insert(to_insert); + } + + // Given an offset of the file, checks if the corresponding block (by considering the file as + // 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset + // within this SortedRangeSet. + // + // For example, the 4106-th byte of a file is from block 1, assuming a block size of 4096-byte. + // The mapped offset within a SortedRangeSet("1-9 15-19") is 10. + // + // An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th + // item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10 + // + 10) in a range represented by this SortedRangeSet. + size_t GetOffsetInRangeSet(size_t old_offset) const { + size_t old_block_start = old_offset / kBlockSize; + size_t new_block_start = 0; + for (const auto& range : ranges_) { + // Find the index of old_block_start. + if (old_block_start >= range.second) { + new_block_start += (range.second - range.first); + } else if (old_block_start >= range.first) { + new_block_start += (old_block_start - range.first); + return (new_block_start * kBlockSize + old_offset % kBlockSize); + } else { + CHECK(false) <<"block_start " << old_block_start << " is missing between two ranges: " + << this->ToString(); + return 0; + } + } + CHECK(false) <<"block_start " << old_block_start << " exceeds the limit of current RangeSet: " + << this->ToString(); + return 0; + } +}; \ No newline at end of file -- cgit v1.2.3 From 5f8dd9951d986b65d98d6a9ea38003427e9e46df Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 28 Jul 2017 00:05:40 -0700 Subject: ui: Move the support for touch inputs into RecoveryUI. - Added detection for EV_ABS events in minui/events.cpp, if it's allowed; - Added listening and processing touch inputs in ui.cpp; - Fixed an issue in recognizing swipe with multi-touch protocol A; - Changed the logic in RecoveryUI::ProcessKey() to be swipe-aware. It now allows turning on text mode with + . The last change also fixed an issue on devices with protocol A: prior to this CL, user may accidentally toggle the text mode during an OTA. Because it was considered as a single-button device, a long tap that sent BTN_TOUCH event would turn on text mode. Test: Allow detecting touch inputs. Swiping (up, down, enter) works on angler, angelfish, dorado respectively. Bug: 36169090 Change-Id: I4bc882b99114ce4ab414f8bdb8f4f7a525b8a8fd --- Android.mk | 12 ++++ minui/events.cpp | 47 +++++++++++---- minui/include/minui/minui.h | 3 +- ui.cpp | 139 ++++++++++++++++++++++++++++++++++++++++++-- ui.h | 24 ++++++++ 5 files changed, 209 insertions(+), 16 deletions(-) diff --git a/Android.mk b/Android.mk index 3eed7a696..967b9dfbe 100644 --- a/Android.mk +++ b/Android.mk @@ -108,6 +108,18 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 endif +ifneq ($(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD),) +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_LOW_THRESHOLD=$(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD) +else +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_LOW_THRESHOLD=50 +endif + +ifneq ($(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD),) +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=$(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD) +else +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=90 +endif + ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) else diff --git a/minui/events.cpp b/minui/events.cpp index 0e1fd44a0..24c2a8277 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -53,36 +53,37 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; } -int ev_init(ev_callback input_cb) { - bool epollctlfail = false; - +int ev_init(ev_callback input_cb, bool allow_touch_inputs) { g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS); if (g_epoll_fd == -1) { return -1; } + bool epollctlfail = false; DIR* dir = opendir("/dev/input"); - if (dir != NULL) { + if (dir != nullptr) { dirent* de; while ((de = readdir(dir))) { - // Use unsigned long to match ioctl's parameter type. - unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT - - // fprintf(stderr,"/dev/input/%s\n", de->d_name); if (strncmp(de->d_name, "event", 5)) continue; int fd = openat(dirfd(dir), de->d_name, O_RDONLY); if (fd == -1) continue; + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT + // Read the evbits of the input device. if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { close(fd); continue; } - // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. + // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also + // allowed if allow_touch_inputs is set. if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { - close(fd); - continue; + if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { + close(fd); + continue; + } } epoll_event ev; @@ -231,3 +232,27 @@ void ev_iterate_available_keys(const std::function& f) { } } } + +void ev_iterate_touch_inputs(const std::function& action) { + for (size_t i = 0; i < ev_dev_count; ++i) { + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {}; // NOLINT + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + continue; + } + if (!test_bit(EV_ABS, ev_bits)) { + continue; + } + + unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)] = {}; // NOLINT + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_ABS, KEY_MAX), key_bits) == -1) { + continue; + } + + for (int key_code = 0; key_code <= KEY_MAX; ++key_code) { + if (test_bit(key_code, key_bits)) { + action(key_code); + } + } + } +} diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index 78dd4cb98..017ddde75 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -74,10 +74,11 @@ struct input_event; using ev_callback = std::function; using ev_set_key_callback = std::function; -int ev_init(ev_callback input_cb); +int ev_init(ev_callback input_cb, bool allow_touch_inputs = false); void ev_exit(); int ev_add_fd(int fd, ev_callback cb); void ev_iterate_available_keys(const std::function& f); +void ev_iterate_touch_inputs(const std::function& action); int ev_sync_key_state(const ev_set_key_callback& set_key_cb); // 'timeout' has the same semantics as poll(2). diff --git a/ui.cpp b/ui.cpp index 30b42a19a..eadcdd433 100644 --- a/ui.cpp +++ b/ui.cpp @@ -54,6 +54,9 @@ RecoveryUI::RecoveryUI() rtl_locale_(false), brightness_normal_(50), brightness_dimmed_(25), + touch_screen_allowed_(false), + kTouchLowThreshold(RECOVERY_UI_TOUCH_LOW_THRESHOLD), + kTouchHighThreshold(RECOVERY_UI_TOUCH_HIGH_THRESHOLD), key_queue_len(0), key_last_down(-1), key_long_press(false), @@ -64,6 +67,8 @@ RecoveryUI::RecoveryUI() has_power_key(false), has_up_key(false), has_down_key(false), + has_touch_screen(false), + touch_slot_(0), screensaver_state_(ScreensaverState::DISABLED) { pthread_mutex_init(&key_queue_mutex, nullptr); pthread_cond_init(&key_queue_cond, nullptr); @@ -77,6 +82,8 @@ void RecoveryUI::OnKeyDetected(int key_code) { has_down_key = true; } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) { has_up_key = true; + } else if (key_code == ABS_MT_POSITION_X || key_code == ABS_MT_POSITION_Y) { + has_touch_screen = true; } } @@ -128,10 +135,15 @@ bool RecoveryUI::Init(const std::string& locale) { // Set up the locale info. SetLocale(locale); - ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2)); + ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2), + touch_screen_allowed_); ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + if (touch_screen_allowed_) { + ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + } + if (!InitScreensaver()) { LOG(INFO) << "Screensaver disabled"; } @@ -140,15 +152,85 @@ bool RecoveryUI::Init(const std::string& locale) { return true; } +void RecoveryUI::OnTouchDetected(int dx, int dy) { + enum SwipeDirection { UP, DOWN, RIGHT, LEFT } direction; + + // We only consider a valid swipe if: + // - the delta along one axis is below kTouchLowThreshold; + // - and the delta along the other axis is beyond kTouchHighThreshold. + if (abs(dy) < kTouchLowThreshold && abs(dx) > kTouchHighThreshold) { + direction = dx < 0 ? SwipeDirection::LEFT : SwipeDirection::RIGHT; + } else if (abs(dx) < kTouchLowThreshold && abs(dy) > kTouchHighThreshold) { + direction = dy < 0 ? SwipeDirection::UP : SwipeDirection::DOWN; + } else { + LOG(DEBUG) << "Ignored " << dx << " " << dy << " (low: " << kTouchLowThreshold + << ", high: " << kTouchHighThreshold << ")"; + return; + } + + LOG(DEBUG) << "Swipe direction=" << direction; + switch (direction) { + case SwipeDirection::UP: + ProcessKey(KEY_UP, 1); // press up key + ProcessKey(KEY_UP, 0); // and release it + break; + + case SwipeDirection::DOWN: + ProcessKey(KEY_DOWN, 1); // press down key + ProcessKey(KEY_DOWN, 0); // and release it + break; + + case SwipeDirection::LEFT: + case SwipeDirection::RIGHT: + ProcessKey(KEY_POWER, 1); // press power key + ProcessKey(KEY_POWER, 0); // and release it + break; + }; +} + int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { struct input_event ev; if (ev_get_input(fd, epevents, &ev) == -1) { return -1; } + // Touch inputs handling. + // + // We handle the touch inputs by tracking the position changes between initial contacting and + // upon lifting. touch_start_X/Y record the initial positions, with touch_finger_down set. Upon + // detecting the lift, we unset touch_finger_down and detect a swipe based on position changes. + // + // Per the doc Multi-touch Protocol at below, there are two protocols. + // https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt + // + // The main difference between the stateless type A protocol and the stateful type B slot protocol + // lies in the usage of identifiable contacts to reduce the amount of data sent to userspace. The + // slot protocol (i.e. type B) sends ABS_MT_TRACKING_ID with a unique id on initial contact, and + // sends ABS_MT_TRACKING_ID -1 upon lifting the contact. Protocol A doesn't send + // ABS_MT_TRACKING_ID -1 on lifting, but the driver may additionally report BTN_TOUCH event. + // + // For protocol A, we rely on BTN_TOUCH to recognize lifting, while for protocol B we look for + // ABS_MT_TRACKING_ID being -1. + // + // Touch input events will only be available if touch_screen_allowed_ is set. + if (ev.type == EV_SYN) { + if (touch_screen_allowed_ && ev.code == SYN_REPORT) { + // There might be multiple SYN_REPORT events. We should only detect a swipe after lifting the + // contact. + if (touch_finger_down_ && !touch_swiping_) { + touch_start_X_ = touch_X_; + touch_start_Y_ = touch_Y_; + touch_swiping_ = true; + } else if (!touch_finger_down_ && touch_swiping_) { + touch_swiping_ = false; + OnTouchDetected(touch_X_ - touch_start_X_, touch_Y_ - touch_start_Y_); + } + } return 0; - } else if (ev.type == EV_REL) { + } + + if (ev.type == EV_REL) { if (ev.code == REL_Y) { // accumulate the up or down motion reported by // the trackball. When it exceeds a threshold @@ -169,7 +251,48 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { rel_sum = 0; } + if (touch_screen_allowed_ && ev.type == EV_ABS) { + if (ev.code == ABS_MT_SLOT) { + touch_slot_ = ev.value; + } + // Ignore other fingers. + if (touch_slot_ > 0) return 0; + + switch (ev.code) { + case ABS_MT_POSITION_X: + touch_X_ = ev.value; + touch_finger_down_ = true; + break; + + case ABS_MT_POSITION_Y: + touch_Y_ = ev.value; + touch_finger_down_ = true; + break; + + case ABS_MT_TRACKING_ID: + // Protocol B: -1 marks lifting the contact. + if (ev.value < 0) touch_finger_down_ = false; + break; + } + return 0; + } + if (ev.type == EV_KEY && ev.code <= KEY_MAX) { + if (touch_screen_allowed_) { + if (ev.code == BTN_TOUCH) { + // A BTN_TOUCH with value 1 indicates the start of contact (protocol A), with 0 means + // lifting the contact. + touch_finger_down_ = (ev.value == 1); + } + + // Intentionally ignore BTN_TOUCH and BTN_TOOL_FINGER, which would otherwise trigger + // additional scrolling (because in ScreenRecoveryUI::ShowFile(), we consider keys other than + // KEY_POWER and KEY_UP as KEY_DOWN). + if (ev.code == BTN_TOUCH || ev.code == BTN_TOOL_FINGER) { + return 0; + } + } + ProcessKey(ev.code, ev.value); } @@ -365,6 +488,14 @@ bool RecoveryUI::HasThreeButtons() { return has_power_key && has_up_key && has_down_key; } +bool RecoveryUI::HasPowerKey() const { + return has_power_key; +} + +bool RecoveryUI::HasTouchScreen() const { + return has_touch_screen; +} + void RecoveryUI::FlushKeys() { pthread_mutex_lock(&key_queue_mutex); key_queue_len = 0; @@ -377,8 +508,8 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { pthread_mutex_unlock(&key_queue_mutex); // If we have power and volume up keys, that chord is the signal to toggle the text display. - if (HasThreeButtons()) { - if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) { + if (HasThreeButtons() || (HasPowerKey() && HasTouchScreen() && touch_screen_allowed_)) { + if ((key == KEY_VOLUMEUP || key == KEY_UP) && IsKeyPressed(KEY_POWER)) { return TOGGLE; } } else { diff --git a/ui.h b/ui.h index 7eb04aec8..5cda7af0f 100644 --- a/ui.h +++ b/ui.h @@ -82,6 +82,12 @@ class RecoveryUI { // otherwise. virtual bool HasThreeButtons(); + // Returns true if it has a power key. + virtual bool HasPowerKey() const; + + // Returns true if it supports touch inputs. + virtual bool HasTouchScreen() const; + // Erases any queued-up keys. virtual void FlushKeys(); @@ -129,7 +135,14 @@ class RecoveryUI { unsigned int brightness_normal_; unsigned int brightness_dimmed_; + // Whether we should listen for touch inputs (default: false). + bool touch_screen_allowed_; + private: + // The sensitivity when detecting a swipe. + const int kTouchLowThreshold; + const int kTouchHighThreshold; + // Key event input queue pthread_mutex_t key_queue_mutex; pthread_cond_t key_queue_cond; @@ -147,6 +160,16 @@ class RecoveryUI { bool has_power_key; bool has_up_key; bool has_down_key; + bool has_touch_screen; + + // Touch event related variables. See the comments in RecoveryUI::OnInputEvent(). + int touch_slot_; + int touch_X_; + int touch_Y_; + int touch_start_X_; + int touch_start_Y_; + bool touch_finger_down_; + bool touch_swiping_; struct key_timer_t { RecoveryUI* ui; @@ -157,6 +180,7 @@ class RecoveryUI { pthread_t input_thread_; void OnKeyDetected(int key_code); + void OnTouchDetected(int dx, int dy); int OnInputEvent(int fd, uint32_t epevents); void ProcessKey(int key_code, int updown); -- cgit v1.2.3 From 6b03ba7902cd97e305d9354fd6a733ab0d2f10fe Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 19 Jul 2017 14:16:30 -0700 Subject: Refactor the imgdiff This helps us to add a new mode to handle large APKs in the follow up CL. Changes include: 1. Create a new interface class 'Image' 1. Create subclasses 'ZipModeImage' and 'ImageModeImage' and move the related functions there. Bug: 63542719 Test: recovery_component_test passes Change-Id: I7729b0ba39b19a9c84811636a60dd0a0b1acc2f0 --- applypatch/Android.mk | 3 +- applypatch/imgdiff.cpp | 959 +++++++++++++++++++++++++++++-------------------- 2 files changed, 569 insertions(+), 393 deletions(-) diff --git a/applypatch/Android.mk b/applypatch/Android.mk index a7412d238..7aed0a95a 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -127,7 +127,8 @@ libimgdiff_src_files := imgdiff.cpp # libbsdiff is compiled with -D_FILE_OFFSET_BITS=64. libimgdiff_cflags := \ -Werror \ - -D_FILE_OFFSET_BITS=64 + -D_FILE_OFFSET_BITS=64 \ + -DZLIB_CONST libimgdiff_static_libraries := \ libbsdiff \ diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index fc240644f..880265260 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -196,7 +196,8 @@ class ImageChunk { size_t DataLengthForPatch() const; void Dump() const { - printf("type %d start %zu len %zu\n", type_, start_, DataLengthForPatch()); + printf("type: %d, start: %zu, len: %zu, name: %s\n", type_, start_, DataLengthForPatch(), + entry_name_.c_str()); } void SetSourceInfo(const ImageChunk& other); @@ -211,7 +212,7 @@ class ImageChunk { size_t GetHeaderSize(size_t patch_size) const; // Return the offset of the next patch into the patch data. - size_t WriteHeaderToFd(int fd, const std::vector& patch, size_t offset); + size_t WriteHeaderToFd(int fd, const std::vector& patch, size_t offset) const; /* * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob @@ -233,6 +234,14 @@ class ImageChunk { bool IsAdjacentNormal(const ImageChunk& other) const; void MergeAdjacentNormal(const ImageChunk& other); + /* + * Compute a bsdiff patch between |this| and the input source chunks. + * Store the result in the patch_data. + * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk is used + * repeatedly, pass nullptr if not needed. + */ + bool MakePatch(const ImageChunk& src, std::vector* patch_data, saidx_t** bsdiff_cache); + private: int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW size_t start_; // offset of chunk in the original input file @@ -322,7 +331,7 @@ bool ImageChunk::ChangeChunkToRaw(size_t patch_size) { void ImageChunk::ChangeDeflateChunkToNormal() { if (type_ != CHUNK_DEFLATE) return; type_ = CHUNK_NORMAL; - entry_name_.clear(); + // No need to clear the entry name. uncompressed_data_.clear(); } @@ -345,7 +354,7 @@ size_t ImageChunk::GetHeaderSize(size_t patch_size) const { } } -size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector& patch, size_t offset) { +size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector& patch, size_t offset) const { Write4(fd, type_); switch (type_) { case CHUNK_NORMAL: @@ -393,6 +402,68 @@ void ImageChunk::MergeAdjacentNormal(const ImageChunk& other) { raw_data_len_ = raw_data_len_ + other.raw_data_len_; } +bool ImageChunk::MakePatch(const ImageChunk& src, std::vector* patch_data, + saidx_t** bsdiff_cache) { + if (ChangeChunkToRaw(0)) { + size_t patch_size = DataLengthForPatch(); + patch_data->resize(patch_size); + std::copy(DataForPatch(), DataForPatch() + patch_size, patch_data->begin()); + return true; + } + +#if defined(__ANDROID__) + char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX"; +#else + char ptemp[] = "/tmp/imgdiff-patch-XXXXXX"; +#endif + + int fd = mkstemp(ptemp); + if (fd == -1) { + printf("MakePatch failed to create a temporary file: %s\n", strerror(errno)); + return false; + } + close(fd); + + int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), DataForPatch(), + DataLengthForPatch(), ptemp, bsdiff_cache); + if (r != 0) { + printf("bsdiff() failed: %d\n", r); + return false; + } + + android::base::unique_fd patch_fd(open(ptemp, O_RDONLY)); + if (patch_fd == -1) { + printf("failed to open %s: %s\n", ptemp, strerror(errno)); + return false; + } + struct stat st; + if (fstat(patch_fd, &st) != 0) { + printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno)); + return false; + } + + size_t sz = static_cast(st.st_size); + // Change the chunk type to raw if the patch takes less space that way. + if (ChangeChunkToRaw(sz)) { + unlink(ptemp); + size_t patch_size = DataLengthForPatch(); + patch_data->resize(patch_size); + std::copy(DataForPatch(), DataForPatch() + patch_size, patch_data->begin()); + return true; + } + patch_data->resize(sz); + if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) { + printf("failed to read \"%s\" %s\n", ptemp, strerror(errno)); + unlink(ptemp); + return false; + } + + unlink(ptemp); + SetSourceInfo(src); + + return true; +} + bool ImageChunk::ReconstructDeflateChunk() { if (type_ != CHUNK_DEFLATE) { printf("attempt to reconstruct non-deflate chunk\n"); @@ -458,195 +529,467 @@ bool ImageChunk::TryReconstruction(int level) { return true; } -// EOCD record -// offset 0: signature 0x06054b50, 4 bytes -// offset 4: number of this disk, 2 bytes -// ... -// offset 20: comment length, 2 bytes -// offset 22: comment, n bytes -static bool GetZipFileSize(const std::vector& zip_file, size_t* input_file_size) { - if (zip_file.size() < 22) { - printf("file is too small to be a zip file\n"); - return false; +// Interface for zip_mode and image_mode images. We initialize the image from an input file and +// split the file content into a list of image chunks. +class Image { + public: + explicit Image(bool is_source) : is_source_(is_source) {} + + virtual ~Image() {} + + // Create a list of image chunks from input file. + virtual bool Initialize(const std::string& filename) = 0; + + // Look for runs of adjacent normal chunks and compress them down into a single chunk. (Such + // runs can be produced when deflate chunks are changed to normal chunks.) + void MergeAdjacentNormalChunks(); + + // In zip mode, find the matching deflate source chunk by entry name. Search for normal chunks + // also if |find_normal| is true. + ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false); + + // Write the contents of |patch_data| to |patch_fd|. + bool WritePatchDataToFd(const std::vector>& patch_data, int patch_fd) const; + + void DumpChunks() const; + + // Non const iterators to access the stored ImageChunks. + std::vector::iterator begin() { + return chunks_.begin(); } - // Look for End of central directory record of the zip file, and calculate the actual - // zip_file size. - for (int i = zip_file.size() - 22; i >= 0; i--) { - if (zip_file[i] == 0x50) { - if (get_unaligned(&zip_file[i]) == 0x06054b50) { - // double-check: this archive consists of a single "disk". - CHECK_EQ(get_unaligned(&zip_file[i + 4]), 0); + std::vector::iterator end() { + return chunks_.end(); + } + // Return a pointer to the ith ImageChunk. + ImageChunk* Get(size_t i) { + CHECK_LT(i, chunks_.size()); + return &chunks_[i]; + } - uint16_t comment_length = get_unaligned(&zip_file[i + 20]); - size_t file_size = i + 22 + comment_length; - CHECK_LE(file_size, zip_file.size()); - *input_file_size = file_size; - return true; + size_t NumOfChunks() const { + return chunks_.size(); + } + + protected: + bool ReadFile(const std::string& filename, std::vector* file_content); + + bool is_source_; // True if it's for source chunks. + std::vector chunks_; // Internal storage of ImageChunk. + std::vector file_content_; // Store the whole input file in memory. +}; + +void Image::MergeAdjacentNormalChunks() { + size_t merged_last = 0, cur = 0; + while (cur < chunks_.size()) { + // Look for normal chunks adjacent to the current one. If such chunk exists, extend the + // length of the current normal chunk. + size_t to_check = cur + 1; + while (to_check < chunks_.size() && chunks_[cur].IsAdjacentNormal(chunks_[to_check])) { + chunks_[cur].MergeAdjacentNormal(chunks_[to_check]); + to_check++; + } + + if (merged_last != cur) { + chunks_[merged_last] = std::move(chunks_[cur]); + } + merged_last++; + cur = to_check; + } + if (merged_last < chunks_.size()) { + chunks_.erase(chunks_.begin() + merged_last, chunks_.end()); + } +} + +ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) { + if (name.empty()) { + return nullptr; + } + for (auto& chunk : chunks_) { + if ((chunk.GetType() == CHUNK_DEFLATE || find_normal) && chunk.GetEntryName() == name) { + return &chunk; + } + } + return nullptr; +} + +bool Image::WritePatchDataToFd(const std::vector>& patch_data, + int patch_fd) const { + // Figure out how big the imgdiff file header is going to be, so that we can correctly compute + // the offset of each bsdiff patch within the file. + CHECK_EQ(chunks_.size(), patch_data.size()); + size_t total_header_size = 12; + for (size_t i = 0; i < chunks_.size(); ++i) { + total_header_size += chunks_[i].GetHeaderSize(patch_data[i].size()); + } + + size_t offset = total_header_size; + + // Write out the headers. + if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) { + printf("failed to write \"IMGDIFF2\": %s\n", strerror(errno)); + return false; + } + Write4(patch_fd, static_cast(chunks_.size())); + for (size_t i = 0; i < chunks_.size(); ++i) { + printf("chunk %zu: ", i); + offset = chunks_[i].WriteHeaderToFd(patch_fd, patch_data[i], offset); + } + + // Append each chunk's bsdiff patch, in order. + for (size_t i = 0; i < chunks_.size(); ++i) { + if (chunks_[i].GetType() != CHUNK_RAW) { + if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) { + printf("failed to write %zu bytes patch for chunk %zu\n", patch_data[i].size(), i); + return false; } } } - // EOCD not found, this file is likely not a valid zip file. - return false; + return true; +} + +void Image::DumpChunks() const { + std::string type = is_source_ ? "source" : "target"; + printf("Dumping chunks for %s\n", type.c_str()); + for (size_t i = 0; i < chunks_.size(); ++i) { + printf("chunk %zu: ", i); + chunks_[i].Dump(); + } } -static bool ReadZip(const char* filename, std::vector* chunks, - std::vector* zip_file, bool include_pseudo_chunk) { - CHECK(chunks != nullptr && zip_file != nullptr); +bool Image::ReadFile(const std::string& filename, std::vector* file_content) { + CHECK(file_content != nullptr); - android::base::unique_fd fd(open(filename, O_RDONLY)); + android::base::unique_fd fd(open(filename.c_str(), O_RDONLY)); if (fd == -1) { - printf("failed to open \"%s\" %s\n", filename, strerror(errno)); + printf("failed to open \"%s\" %s\n", filename.c_str(), strerror(errno)); return false; } struct stat st; if (fstat(fd, &st) != 0) { - printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); + printf("failed to stat \"%s\": %s\n", filename.c_str(), strerror(errno)); return false; } size_t sz = static_cast(st.st_size); - zip_file->resize(sz); - if (!android::base::ReadFully(fd, zip_file->data(), sz)) { - printf("failed to read \"%s\" %s\n", filename, strerror(errno)); + file_content->resize(sz); + if (!android::base::ReadFully(fd, file_content->data(), sz)) { + printf("failed to read \"%s\" %s\n", filename.c_str(), strerror(errno)); return false; } fd.reset(); - // Trim the trailing zeros before we pass the file to ziparchive handler. + return true; +} + +class ZipModeImage : public Image { + public: + explicit ZipModeImage(bool is_source) : Image(is_source) {} + + bool Initialize(const std::string& filename) override; + + const ImageChunk& PseudoSource() const { + CHECK(is_source_); + CHECK(pseudo_source_ != nullptr); + return *pseudo_source_; + } + + // Verify that we can reconstruct the deflate chunks; also change the type to CHUNK_NORMAL if + // src and tgt are identical. + static bool CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image); + + // Compute the patches against the input image, and write the data into |patch_name|. + static bool GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_image, + const std::string& patch_name); + + private: + // Initialize image chunks based on the zip entries. + bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle); + // Add the a zip entry to the list. + bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry); + // Return the real size of the zip file. (omit the trailing zeros that used for alignment) + bool GetZipFileSize(size_t* input_file_size); + + // The pesudo source chunk for bsdiff if there's no match for the given target chunk. It's in + // fact the whole source file. + std::unique_ptr pseudo_source_; +}; + +bool ZipModeImage::Initialize(const std::string& filename) { + if (!ReadFile(filename, &file_content_)) { + return false; + } + + // Omit the trailing zeros before we pass the file to ziparchive handler. size_t zipfile_size; - if (!GetZipFileSize(*zip_file, &zipfile_size)) { - printf("failed to parse the actual size of %s\n", filename); + if (!GetZipFileSize(&zipfile_size)) { + printf("failed to parse the actual size of %s\n", filename.c_str()); return false; } ZipArchiveHandle handle; - int err = OpenArchiveFromMemory(zip_file->data(), zipfile_size, filename, &handle); + int err = OpenArchiveFromMemory(const_cast(file_content_.data()), zipfile_size, + filename.c_str(), &handle); if (err != 0) { - printf("failed to open zip file %s: %s\n", filename, ErrorCodeString(err)); + printf("failed to open zip file %s: %s\n", filename.c_str(), ErrorCodeString(err)); CloseArchive(handle); return false; } - // Create a list of deflated zip entries, sorted by offset. - std::vector> temp_entries; + if (is_source_) { + pseudo_source_ = std::make_unique(CHUNK_NORMAL, 0, &file_content_, zipfile_size); + } + if (!InitializeChunks(filename, handle)) { + CloseArchive(handle); + return false; + } + + CloseArchive(handle); + return true; +} + +// Iterate the zip entries and compose the image chunks accordingly. +bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandle handle) { void* cookie; int ret = StartIteration(handle, &cookie, nullptr, nullptr); if (ret != 0) { - printf("failed to iterate over entries in %s: %s\n", filename, ErrorCodeString(ret)); - CloseArchive(handle); + printf("failed to iterate over entries in %s: %s\n", filename.c_str(), ErrorCodeString(ret)); return false; } + // Create a list of deflated zip entries, sorted by offset. + std::vector> temp_entries; ZipString name; ZipEntry entry; while ((ret = Next(cookie, &entry, &name)) == 0) { if (entry.method == kCompressDeflated) { - std::string entryname(name.name, name.name + name.name_length); - temp_entries.push_back(std::make_pair(entryname, entry)); + std::string entry_name(name.name, name.name + name.name_length); + temp_entries.emplace_back(entry_name, entry); } } if (ret != -1) { printf("Error while iterating over zip entries: %s\n", ErrorCodeString(ret)); - CloseArchive(handle); return false; } std::sort(temp_entries.begin(), temp_entries.end(), - [](auto& entry1, auto& entry2) { - return entry1.second.offset < entry2.second.offset; - }); + [](auto& entry1, auto& entry2) { return entry1.second.offset < entry2.second.offset; }); EndIteration(cookie); - if (include_pseudo_chunk) { - chunks->emplace_back(CHUNK_NORMAL, 0, zip_file, zip_file->size()); + // For source chunks, we don't need to compose chunks for the metadata. + if (is_source_) { + for (auto& entry : temp_entries) { + if (!AddZipEntryToChunks(handle, entry.first, &entry.second)) { + printf("Failed to add %s to source chunks\n", entry.first.c_str()); + return false; + } + } + return true; } + // For target chunks, add the deflate entries as CHUNK_DEFLATE and the contents between two + // deflate entries as CHUNK_NORMAL. size_t pos = 0; size_t nextentry = 0; - while (pos < zip_file->size()) { + while (pos < file_content_.size()) { if (nextentry < temp_entries.size() && static_cast(pos) == temp_entries[nextentry].second.offset) { - // compose the next deflate chunk. - std::string entryname = temp_entries[nextentry].first; - size_t uncompressed_len = temp_entries[nextentry].second.uncompressed_length; - std::vector uncompressed_data(uncompressed_len); - if ((ret = ExtractToMemory(handle, &temp_entries[nextentry].second, uncompressed_data.data(), - uncompressed_len)) != 0) { - printf("failed to extract %s with size %zu: %s\n", entryname.c_str(), uncompressed_len, - ErrorCodeString(ret)); - CloseArchive(handle); + // Add the next zip entry. + std::string entry_name = temp_entries[nextentry].first; + if (!AddZipEntryToChunks(handle, entry_name, &temp_entries[nextentry].second)) { + printf("Failed to add %s to target chunks\n", entry_name.c_str()); return false; } - size_t compressed_len = temp_entries[nextentry].second.compressed_length; - ImageChunk curr(CHUNK_DEFLATE, pos, zip_file, compressed_len); - curr.SetEntryName(std::move(entryname)); - curr.SetUncompressedData(std::move(uncompressed_data)); - chunks->push_back(curr); - - pos += compressed_len; + pos += temp_entries[nextentry].second.compressed_length; ++nextentry; continue; } - // Use a normal chunk to take all the data up to the start of the next deflate section. + // Use a normal chunk to take all the data up to the start of the next entry. size_t raw_data_len; if (nextentry < temp_entries.size()) { raw_data_len = temp_entries[nextentry].second.offset - pos; } else { - raw_data_len = zip_file->size() - pos; + raw_data_len = file_content_.size() - pos; } - chunks->emplace_back(CHUNK_NORMAL, pos, zip_file, raw_data_len); + chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, raw_data_len); pos += raw_data_len; } - CloseArchive(handle); return true; } -// Read the given file and break it up into chunks, and putting the data in to a vector. -static bool ReadImage(const char* filename, std::vector* chunks, - std::vector* img) { - CHECK(chunks != nullptr && img != nullptr); +bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, + ZipEntry* entry) { + size_t compressed_len = entry->compressed_length; + if (entry->method == kCompressDeflated) { + size_t uncompressed_len = entry->uncompressed_length; + std::vector uncompressed_data(uncompressed_len); + int ret = ExtractToMemory(handle, entry, uncompressed_data.data(), uncompressed_len); + if (ret != 0) { + printf("failed to extract %s with size %zu: %s\n", entry_name.c_str(), uncompressed_len, + ErrorCodeString(ret)); + return false; + } + ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len); + curr.SetEntryName(entry_name); + curr.SetUncompressedData(std::move(uncompressed_data)); + chunks_.push_back(curr); + } else { + ImageChunk curr(CHUNK_NORMAL, entry->offset, &file_content_, compressed_len); + curr.SetEntryName(entry_name); + chunks_.push_back(curr); + } - android::base::unique_fd fd(open(filename, O_RDONLY)); - if (fd == -1) { - printf("failed to open \"%s\" %s\n", filename, strerror(errno)); + return true; +} + +// EOCD record +// offset 0: signature 0x06054b50, 4 bytes +// offset 4: number of this disk, 2 bytes +// ... +// offset 20: comment length, 2 bytes +// offset 22: comment, n bytes +bool ZipModeImage::GetZipFileSize(size_t* input_file_size) { + if (file_content_.size() < 22) { + printf("file is too small to be a zip file\n"); return false; } - struct stat st; - if (fstat(fd, &st) != 0) { - printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); + + // Look for End of central directory record of the zip file, and calculate the actual + // zip_file size. + for (int i = file_content_.size() - 22; i >= 0; i--) { + if (file_content_[i] == 0x50) { + if (get_unaligned(&file_content_[i]) == 0x06054b50) { + // double-check: this archive consists of a single "disk". + CHECK_EQ(get_unaligned(&file_content_[i + 4]), 0); + + uint16_t comment_length = get_unaligned(&file_content_[i + 20]); + size_t file_size = i + 22 + comment_length; + CHECK_LE(file_size, file_content_.size()); + *input_file_size = file_size; + return true; + } + } + } + + // EOCD not found, this file is likely not a valid zip file. + return false; +} + +bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image) { + for (auto& tgt_chunk : *tgt_image) { + if (tgt_chunk.GetType() != CHUNK_DEFLATE) { + continue; + } + + ImageChunk* src_chunk = src_image->FindChunkByName(tgt_chunk.GetEntryName()); + if (src_chunk == nullptr) { + tgt_chunk.ChangeDeflateChunkToNormal(); + } else if (tgt_chunk == *src_chunk) { + // If two deflate chunks are identical (eg, the kernel has not changed between two builds), + // treat them as normal chunks. This makes applypatch much faster -- it can apply a trivial + // patch to the compressed data, rather than uncompressing and recompressing to apply the + // trivial patch to the uncompressed data. + tgt_chunk.ChangeDeflateChunkToNormal(); + src_chunk->ChangeDeflateChunkToNormal(); + } else if (!tgt_chunk.ReconstructDeflateChunk()) { + // We cannot recompress the data and get exactly the same bits as are in the input target + // image. Treat the chunk as a normal non-deflated chunk. + printf("failed to reconstruct target deflate chunk [%s]; treating as normal\n", + tgt_chunk.GetEntryName().c_str()); + + tgt_chunk.ChangeDeflateChunkToNormal(); + src_chunk->ChangeDeflateChunkToNormal(); + } + } + + return true; +} + +bool ZipModeImage::GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_image, + const std::string& patch_name) { + // For zips, we only need merge normal chunks for the target: deflated chunks are matched via + // filename, and normal chunks are patched using the entire source file as the source. + tgt_image->MergeAdjacentNormalChunks(); + tgt_image->DumpChunks(); + + printf("Construct patches for %zu chunks...\n", tgt_image->NumOfChunks()); + std::vector> patch_data(tgt_image->NumOfChunks()); + + saidx_t* bsdiff_cache = nullptr; + size_t i = 0; + for (auto& tgt_chunk : *tgt_image) { + ImageChunk* src_chunk = (tgt_chunk.GetType() != CHUNK_DEFLATE) + ? nullptr + : src_image->FindChunkByName(tgt_chunk.GetEntryName()); + + const auto& src_ref = (src_chunk == nullptr) ? src_image->PseudoSource() : *src_chunk; + saidx_t** bsdiff_cache_ptr = (src_chunk == nullptr) ? &bsdiff_cache : nullptr; + + if (!tgt_chunk.MakePatch(src_ref, &patch_data[i], bsdiff_cache_ptr)) { + printf("Failed to generate patch, name: %s\n", tgt_chunk.GetEntryName().c_str()); + return false; + } + + printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(), + tgt_chunk.GetRawDataLength()); + i++; + } + free(bsdiff_cache); + + android::base::unique_fd patch_fd( + open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); + if (patch_fd == -1) { + printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno)); return false; } - size_t sz = static_cast(st.st_size); - img->resize(sz); - if (!android::base::ReadFully(fd, img->data(), sz)) { - printf("failed to read \"%s\" %s\n", filename, strerror(errno)); + return tgt_image->WritePatchDataToFd(patch_data, patch_fd); +} + +class ImageModeImage : public Image { + public: + explicit ImageModeImage(bool is_source) : Image(is_source) {} + + // Initialize the image chunks list by searching the magic numbers in an image file. + bool Initialize(const std::string& filename) override; + + // In Image Mode, verify that the source and target images have the same chunk structure (ie, the + // same sequence of deflate and normal chunks). + static bool CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image); + + // In image mode, generate patches against the given source chunks and bonus_data; write the + // result to |patch_name|. + static bool GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* src_image, + const std::vector& bonus_data, const std::string& patch_name); +}; + +bool ImageModeImage::Initialize(const std::string& filename) { + if (!ReadFile(filename, &file_content_)) { return false; } + size_t sz = file_content_.size(); size_t pos = 0; - while (pos < sz) { // 0x00 no header flags, 0x08 deflate compression, 0x1f8b gzip magic number - if (sz - pos >= 4 && get_unaligned(img->data() + pos) == 0x00088b1f) { + if (sz - pos >= 4 && get_unaligned(file_content_.data() + pos) == 0x00088b1f) { // 'pos' is the offset of the start of a gzip chunk. size_t chunk_offset = pos; // The remaining data is too small to be a gzip chunk; treat them as a normal chunk. if (sz - pos < GZIP_HEADER_LEN + GZIP_FOOTER_LEN) { - chunks->emplace_back(CHUNK_NORMAL, pos, img, sz - pos); + chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, sz - pos); break; } // We need three chunks for the deflated image in total, one normal chunk for the header, // one deflated chunk for the body, and another normal chunk for the footer. - chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_HEADER_LEN); + chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, GZIP_HEADER_LEN); pos += GZIP_HEADER_LEN; // We must decompress this chunk in order to discover where it ends, and so we can update @@ -657,7 +1000,7 @@ static bool ReadImage(const char* filename, std::vector* chunks, strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = sz - pos; - strm.next_in = img->data() + pos; + strm.next_in = file_content_.data() + pos; // -15 means we are decoding a 'raw' deflate stream; zlib will // not expect zlib headers. @@ -700,22 +1043,22 @@ static bool ReadImage(const char* filename, std::vector* chunks, printf("Warning: invalid footer position; treating as a nomal chunk\n"); continue; } - size_t footer_size = get_unaligned(img->data() + footer_index); + size_t footer_size = get_unaligned(file_content_.data() + footer_index); if (footer_size != uncompressed_len) { printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n", footer_size, uncompressed_len); continue; } - ImageChunk body(CHUNK_DEFLATE, pos, img, raw_data_len); + ImageChunk body(CHUNK_DEFLATE, pos, &file_content_, raw_data_len); uncompressed_data.resize(uncompressed_len); body.SetUncompressedData(std::move(uncompressed_data)); - chunks->push_back(body); + chunks_.push_back(body); pos += raw_data_len; // create a normal chunk for the footer - chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_FOOTER_LEN); + chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, GZIP_FOOTER_LEN); pos += GZIP_FOOTER_LEN; } else { @@ -726,12 +1069,12 @@ static bool ReadImage(const char* filename, std::vector* chunks, size_t data_len = 0; while (data_len + pos < sz) { if (data_len + pos + 4 <= sz && - get_unaligned(img->data() + pos + data_len) == 0x00088b1f) { + get_unaligned(file_content_.data() + pos + data_len) == 0x00088b1f) { break; } data_len++; } - chunks->emplace_back(CHUNK_NORMAL, pos, img, data_len); + chunks_.emplace_back(CHUNK_NORMAL, pos, &file_content_, data_len); pos += data_len; } @@ -740,346 +1083,178 @@ static bool ReadImage(const char* filename, std::vector* chunks, return true; } -/* - * Given source and target chunks, compute a bsdiff patch between them. - * Store the result in the patch_data. - * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk - * is used repeatedly, pass nullptr if not needed. - */ -static bool MakePatch(const ImageChunk* src, ImageChunk* tgt, std::vector* patch_data, - saidx_t** bsdiff_cache) { - if (tgt->ChangeChunkToRaw(0)) { - size_t patch_size = tgt->DataLengthForPatch(); - patch_data->resize(patch_size); - std::copy(tgt->DataForPatch(), tgt->DataForPatch() + patch_size, patch_data->begin()); - return true; - } - -#if defined(__ANDROID__) - char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX"; -#else - char ptemp[] = "/tmp/imgdiff-patch-XXXXXX"; -#endif - - int fd = mkstemp(ptemp); - if (fd == -1) { - printf("MakePatch failed to create a temporary file: %s\n", strerror(errno)); +// In Image Mode, verify that the source and target images have the same chunk structure (ie, the +// same sequence of deflate and normal chunks). +bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image) { + // In image mode, merge the gzip header and footer in with any adjacent normal chunks. + tgt_image->MergeAdjacentNormalChunks(); + src_image->MergeAdjacentNormalChunks(); + + if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) { + printf("source and target don't have same number of chunks!\n"); + tgt_image->DumpChunks(); + src_image->DumpChunks(); return false; } - close(fd); - - int r = bsdiff::bsdiff(src->DataForPatch(), src->DataLengthForPatch(), tgt->DataForPatch(), - tgt->DataLengthForPatch(), ptemp, bsdiff_cache); - if (r != 0) { - printf("bsdiff() failed: %d\n", r); - return false; + for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) { + if (tgt_image->Get(i)->GetType() != src_image->Get(i)->GetType()) { + printf("source and target don't have same chunk structure! (chunk %zu)\n", i); + tgt_image->DumpChunks(); + src_image->DumpChunks(); + return false; + } } - android::base::unique_fd patch_fd(open(ptemp, O_RDONLY)); - if (patch_fd == -1) { - printf("failed to open %s: %s\n", ptemp, strerror(errno)); - return false; - } - struct stat st; - if (fstat(patch_fd, &st) != 0) { - printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno)); - return false; - } + for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) { + auto& tgt_chunk = *tgt_image->Get(i); + auto& src_chunk = *src_image->Get(i); + if (tgt_chunk.GetType() != CHUNK_DEFLATE) { + continue; + } - size_t sz = static_cast(st.st_size); - // Change the chunk type to raw if the patch takes less space that way. - if (tgt->ChangeChunkToRaw(sz)) { - unlink(ptemp); - size_t patch_size = tgt->DataLengthForPatch(); - patch_data->resize(patch_size); - std::copy(tgt->DataForPatch(), tgt->DataForPatch() + patch_size, patch_data->begin()); - return true; + // Confirm that we can recompress the data and get exactly the same bits as are in the + // input target image. + if (!tgt_chunk.ReconstructDeflateChunk()) { + printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i, + tgt_chunk.GetEntryName().c_str()); + tgt_chunk.ChangeDeflateChunkToNormal(); + src_chunk.ChangeDeflateChunkToNormal(); + continue; + } + + // If two deflate chunks are identical treat them as normal chunks. + if (tgt_chunk == src_chunk) { + tgt_chunk.ChangeDeflateChunkToNormal(); + src_chunk.ChangeDeflateChunkToNormal(); + } } - patch_data->resize(sz); - if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) { - printf("failed to read \"%s\" %s\n", ptemp, strerror(errno)); + + // For images, we need to maintain the parallel structure of the chunk lists, so do the merging + // in both the source and target lists. + tgt_image->MergeAdjacentNormalChunks(); + src_image->MergeAdjacentNormalChunks(); + if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) { + // This shouldn't happen. + printf("merging normal chunks went awry\n"); return false; } - unlink(ptemp); - tgt->SetSourceInfo(*src); - return true; } -/* - * Look for runs of adjacent normal chunks and compress them down into - * a single chunk. (Such runs can be produced when deflate chunks are - * changed to normal chunks.) - */ -static void MergeAdjacentNormalChunks(std::vector* chunks) { - size_t merged_last = 0, cur = 0; - while (cur < chunks->size()) { - // Look for normal chunks adjacent to the current one. If such chunk exists, extend the - // length of the current normal chunk. - size_t to_check = cur + 1; - while (to_check < chunks->size() && chunks->at(cur).IsAdjacentNormal(chunks->at(to_check))) { - chunks->at(cur).MergeAdjacentNormal(chunks->at(to_check)); - to_check++; +// In image mode, generate patches against the given source chunks and bonus_data; write the +// result to |patch_name|. +bool ImageModeImage::GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* src_image, + const std::vector& bonus_data, + const std::string& patch_name) { + printf("Construct patches for %zu chunks...\n", tgt_image->NumOfChunks()); + std::vector> patch_data(tgt_image->NumOfChunks()); + + for (size_t i = 0; i < tgt_image->NumOfChunks(); i++) { + auto& tgt_chunk = *tgt_image->Get(i); + auto& src_chunk = *src_image->Get(i); + + if (i == 1 && !bonus_data.empty()) { + printf(" using %zu bytes of bonus data for chunk %zu\n", bonus_data.size(), i); + src_chunk.SetBonusData(bonus_data); } - if (merged_last != cur) { - chunks->at(merged_last) = std::move(chunks->at(cur)); + if (!tgt_chunk.MakePatch(src_chunk, &patch_data[i], nullptr)) { + printf("Failed to generate patch for target chunk %zu: ", i); + return false; } - merged_last++; - cur = to_check; - } - if (merged_last < chunks->size()) { - chunks->erase(chunks->begin() + merged_last, chunks->end()); + printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(), + tgt_chunk.GetRawDataLength()); } -} -static ImageChunk* FindChunkByName(const std::string& name, std::vector& chunks) { - for (size_t i = 0; i < chunks.size(); ++i) { - if (chunks[i].GetType() == CHUNK_DEFLATE && chunks[i].GetEntryName() == name) { - return &chunks[i]; - } + android::base::unique_fd patch_fd( + open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); + if (patch_fd == -1) { + printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno)); + return false; } - return nullptr; -} -static void DumpChunks(const std::vector& chunks) { - for (size_t i = 0; i < chunks.size(); ++i) { - printf("chunk %zu: ", i); - chunks[i].Dump(); - } + return tgt_image->WritePatchDataToFd(patch_data, patch_fd); } int imgdiff(int argc, const char** argv) { bool zip_mode = false; + std::vector bonus_data; - if (argc >= 2 && strcmp(argv[1], "-z") == 0) { - zip_mode = true; - --argc; - ++argv; - } + int opt; + optind = 1; // Reset the getopt state so that we can call it multiple times for test. - std::vector bonus_data; - if (argc >= 3 && strcmp(argv[1], "-b") == 0) { - android::base::unique_fd fd(open(argv[2], O_RDONLY)); - if (fd == -1) { - printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno)); - return 1; - } - struct stat st; - if (fstat(fd, &st) != 0) { - printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno)); - return 1; - } + while ((opt = getopt(argc, const_cast(argv), "zb:")) != -1) { + switch (opt) { + case 'z': + zip_mode = true; + break; + case 'b': { + android::base::unique_fd fd(open(optarg, O_RDONLY)); + if (fd == -1) { + printf("failed to open bonus file %s: %s\n", optarg, strerror(errno)); + return 1; + } + struct stat st; + if (fstat(fd, &st) != 0) { + printf("failed to stat bonus file %s: %s\n", optarg, strerror(errno)); + return 1; + } - size_t bonus_size = st.st_size; - bonus_data.resize(bonus_size); - if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) { - printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno)); - return 1; + size_t bonus_size = st.st_size; + bonus_data.resize(bonus_size); + if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) { + printf("failed to read bonus file %s: %s\n", optarg, strerror(errno)); + return 1; + } + break; + } + default: + printf("unexpected opt: %s\n", optarg); + return 2; } - - argc -= 2; - argv += 2; } - if (argc != 4) { - printf("usage: %s [-z] [-b ] \n", - argv[0]); + if (argc - optind != 3) { + printf("usage: %s [-z] [-b ] \n", argv[0]); return 2; } - std::vector src_chunks; - std::vector tgt_chunks; - std::vector src_file; - std::vector tgt_file; - if (zip_mode) { - if (!ReadZip(argv[1], &src_chunks, &src_file, true)) { - printf("failed to break apart source zip file\n"); + ZipModeImage src_image(true); + ZipModeImage tgt_image(false); + + if (!src_image.Initialize(argv[optind])) { return 1; } - if (!ReadZip(argv[2], &tgt_chunks, &tgt_file, false)) { - printf("failed to break apart target zip file\n"); + if (!tgt_image.Initialize(argv[optind + 1])) { return 1; } - } else { - if (!ReadImage(argv[1], &src_chunks, &src_file)) { - printf("failed to break apart source image\n"); + + if (!ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) { return 1; } - if (!ReadImage(argv[2], &tgt_chunks, &tgt_file)) { - printf("failed to break apart target image\n"); + // Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of + // deflate chunks). + if (!ZipModeImage::GeneratePatches(&tgt_image, &src_image, argv[optind + 2])) { return 1; } + } else { + ImageModeImage src_image(true); + ImageModeImage tgt_image(false); - // Verify that the source and target images have the same chunk - // structure (ie, the same sequence of deflate and normal chunks). - - // Merge the gzip header and footer in with any adjacent normal chunks. - MergeAdjacentNormalChunks(&tgt_chunks); - MergeAdjacentNormalChunks(&src_chunks); - - if (src_chunks.size() != tgt_chunks.size()) { - printf("source and target don't have same number of chunks!\n"); - printf("source chunks:\n"); - DumpChunks(src_chunks); - printf("target chunks:\n"); - DumpChunks(tgt_chunks); + if (!src_image.Initialize(argv[optind])) { return 1; } - for (size_t i = 0; i < src_chunks.size(); ++i) { - if (src_chunks[i].GetType() != tgt_chunks[i].GetType()) { - printf("source and target don't have same chunk structure! (chunk %zu)\n", i); - printf("source chunks:\n"); - DumpChunks(src_chunks); - printf("target chunks:\n"); - DumpChunks(tgt_chunks); - return 1; - } - } - } - - for (size_t i = 0; i < tgt_chunks.size(); ++i) { - if (tgt_chunks[i].GetType() == CHUNK_DEFLATE) { - // Confirm that given the uncompressed chunk data in the target, we - // can recompress it and get exactly the same bits as are in the - // input target image. If this fails, treat the chunk as a normal - // non-deflated chunk. - if (!tgt_chunks[i].ReconstructDeflateChunk()) { - printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i, - tgt_chunks[i].GetEntryName().c_str()); - tgt_chunks[i].ChangeDeflateChunkToNormal(); - if (zip_mode) { - ImageChunk* src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks); - if (src != nullptr) { - src->ChangeDeflateChunkToNormal(); - } - } else { - src_chunks[i].ChangeDeflateChunkToNormal(); - } - continue; - } - - // If two deflate chunks are identical (eg, the kernel has not - // changed between two builds), treat them as normal chunks. - // This makes applypatch much faster -- it can apply a trivial - // patch to the compressed data, rather than uncompressing and - // recompressing to apply the trivial patch to the uncompressed - // data. - ImageChunk* src; - if (zip_mode) { - src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks); - } else { - src = &src_chunks[i]; - } - - if (src == nullptr) { - tgt_chunks[i].ChangeDeflateChunkToNormal(); - } else if (tgt_chunks[i] == *src) { - tgt_chunks[i].ChangeDeflateChunkToNormal(); - src->ChangeDeflateChunkToNormal(); - } - } - } - - // Merging neighboring normal chunks. - if (zip_mode) { - // For zips, we only need to do this to the target: deflated - // chunks are matched via filename, and normal chunks are patched - // using the entire source file as the source. - MergeAdjacentNormalChunks(&tgt_chunks); - - } else { - // For images, we need to maintain the parallel structure of the - // chunk lists, so do the merging in both the source and target - // lists. - MergeAdjacentNormalChunks(&tgt_chunks); - MergeAdjacentNormalChunks(&src_chunks); - if (src_chunks.size() != tgt_chunks.size()) { - // This shouldn't happen. - printf("merging normal chunks went awry\n"); + if (!tgt_image.Initialize(argv[optind + 1])) { return 1; } - } - - // Compute bsdiff patches for each chunk's data (the uncompressed - // data, in the case of deflate chunks). - - DumpChunks(src_chunks); - printf("Construct patches for %zu chunks...\n", tgt_chunks.size()); - std::vector> patch_data(tgt_chunks.size()); - saidx_t* bsdiff_cache = nullptr; - for (size_t i = 0; i < tgt_chunks.size(); ++i) { - if (zip_mode) { - ImageChunk* src; - if (tgt_chunks[i].GetType() == CHUNK_DEFLATE && - (src = FindChunkByName(tgt_chunks[i].GetEntryName(), src_chunks))) { - if (!MakePatch(src, &tgt_chunks[i], &patch_data[i], nullptr)) { - printf("Failed to generate patch for target chunk %zu: ", i); - return 1; - } - } else { - if (!MakePatch(&src_chunks[0], &tgt_chunks[i], &patch_data[i], &bsdiff_cache)) { - printf("Failed to generate patch for target chunk %zu: ", i); - return 1; - } - } - } else { - if (i == 1 && !bonus_data.empty()) { - printf(" using %zu bytes of bonus data for chunk %zu\n", bonus_data.size(), i); - src_chunks[i].SetBonusData(bonus_data); - } - - if (!MakePatch(&src_chunks[i], &tgt_chunks[i], &patch_data[i], nullptr)) { - printf("Failed to generate patch for target chunk %zu: ", i); - return 1; - } + if (!ImageModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) { + return 1; } - printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(), - src_chunks[i].GetRawDataLength()); - } - - if (bsdiff_cache != nullptr) { - free(bsdiff_cache); - } - - // Figure out how big the imgdiff file header is going to be, so - // that we can correctly compute the offset of each bsdiff patch - // within the file. - - size_t total_header_size = 12; - for (size_t i = 0; i < tgt_chunks.size(); ++i) { - total_header_size += tgt_chunks[i].GetHeaderSize(patch_data[i].size()); - } - - size_t offset = total_header_size; - - android::base::unique_fd patch_fd(open(argv[3], O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); - if (patch_fd == -1) { - printf("failed to open \"%s\": %s\n", argv[3], strerror(errno)); - return 1; - } - - // Write out the headers. - if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) { - printf("failed to write \"IMGDIFF2\" to \"%s\": %s\n", argv[3], strerror(errno)); - return 1; - } - Write4(patch_fd, static_cast(tgt_chunks.size())); - for (size_t i = 0; i < tgt_chunks.size(); ++i) { - printf("chunk %zu: ", i); - offset = tgt_chunks[i].WriteHeaderToFd(patch_fd, patch_data[i], offset); - } - - // Append each chunk's bsdiff patch, in order. - for (size_t i = 0; i < tgt_chunks.size(); ++i) { - if (tgt_chunks[i].GetType() != CHUNK_RAW) { - if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) { - CHECK(false) << "failed to write " << patch_data[i].size() << " bytes patch for chunk " - << i; - } + if (!ImageModeImage::GeneratePatches(&tgt_image, &src_image, bonus_data, argv[optind + 2])) { + return 1; } } -- cgit v1.2.3 From 046aae29d9b0d2cdf24ad0567146991c3864c140 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 31 Jul 2017 23:15:09 -0700 Subject: ui: Check for bootreason=recovery_ui. Some wear bootloaders are passing bootreason=recovery_ui when booting into recovery from fastboot, or via 'adb reboot recovery'. Allow turning on text mode with a swipe for such a bootreason. Since we will turn on text mode automatically for debuggable builds, this bootreason mainly handles the case for user builds. Note this change only applies to devices that allow touch screen inputs. Bug: 36169090 Test: Build and boot into user build recovery image. Toggle on text mode with a swipe. Change-Id: I55f19aed7b210352f8370de19935b4772cc12095 --- ui.cpp | 20 ++++++++++++++++++++ ui.h | 1 + 2 files changed, 21 insertions(+) diff --git a/ui.cpp b/ui.cpp index eadcdd433..e80d7ed04 100644 --- a/ui.cpp +++ b/ui.cpp @@ -69,6 +69,7 @@ RecoveryUI::RecoveryUI() has_down_key(false), has_touch_screen(false), touch_slot_(0), + is_bootreason_recovery_ui_(false), screensaver_state_(ScreensaverState::DISABLED) { pthread_mutex_init(&key_queue_mutex, nullptr); pthread_cond_init(&key_queue_cond, nullptr); @@ -142,6 +143,19 @@ bool RecoveryUI::Init(const std::string& locale) { if (touch_screen_allowed_) { ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + + // Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of + // "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way + // to turn on text mode. It will only be set if the recovery boot is triggered from fastboot, or + // with 'adb reboot recovery'. Note that this applies to all build variants. Otherwise the text + // mode will be turned on automatically on debuggable builds, even without a swipe. + std::string cmdline; + if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) { + is_bootreason_recovery_ui_ = cmdline.find("bootreason=recovery_ui") != std::string::npos; + } else { + // Non-fatal, and won't affect Init() result. + PLOG(WARNING) << "Failed to read /proc/cmdline"; + } } if (!InitScreensaver()) { @@ -168,6 +182,12 @@ void RecoveryUI::OnTouchDetected(int dx, int dy) { return; } + // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui. + if (is_bootreason_recovery_ui_ && !IsTextVisible()) { + ShowText(true); + return; + } + LOG(DEBUG) << "Swipe direction=" << direction; switch (direction) { case SwipeDirection::UP: diff --git a/ui.h b/ui.h index 5cda7af0f..3d9afece0 100644 --- a/ui.h +++ b/ui.h @@ -170,6 +170,7 @@ class RecoveryUI { int touch_start_Y_; bool touch_finger_down_; bool touch_swiping_; + bool is_bootreason_recovery_ui_; struct key_timer_t { RecoveryUI* ui; -- cgit v1.2.3 From 1a0929cc8aac532dba00b3c98cea22715719a421 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 7 Aug 2017 18:47:27 -0400 Subject: update_verifier: Support androidboot.veritymode being empty or 'disabled'. Bootloaders using libavb will set androidboot.veritymode=disabled if the "disable dm-verity" flag has been set. Additionally if the "disable verification" flag is set androidboot.veritymode will not be set at all. Handle both cases. Without this fix we'll end up in a bootloop. Test: Manually tested on a device using AVB. Bug: 64315394 Change-Id: I8310849e347248f4a96158838310f688ecef4211 --- update_verifier/update_verifier.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index b49011a12..4c3cc46cf 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -252,23 +252,36 @@ int update_verifier(int argc, char** argv) { // The current slot has not booted successfully. #if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE) + bool skip_verification = false; std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); if (verity_mode.empty()) { + // With AVB it's possible to disable verification entirely and + // in this case ro.boot.veritymode is empty. +#if defined(BOARD_AVB_ENABLE) + LOG(WARNING) << "verification has been disabled; marking without verification."; + skip_verification = true; +#else LOG(ERROR) << "Failed to get dm-verity mode."; return reboot_device(); +#endif } else if (android::base::EqualsIgnoreCase(verity_mode, "eio")) { // We shouldn't see verity in EIO mode if the current slot hasn't booted successfully before. // Continue the verification until we fail to read some blocks. LOG(WARNING) << "Found dm-verity in EIO mode."; + } else if (android::base::EqualsIgnoreCase(verity_mode, "disabled")) { + LOG(WARNING) << "dm-verity in disabled mode; marking without verification."; + skip_verification = true; } else if (verity_mode != "enforcing") { LOG(ERROR) << "Unexpected dm-verity mode : " << verity_mode << ", expecting enforcing."; return reboot_device(); } - static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; - if (!verify_image(CARE_MAP_FILE)) { - LOG(ERROR) << "Failed to verify all blocks in care map file."; - return reboot_device(); + if (!skip_verification) { + static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; + if (!verify_image(CARE_MAP_FILE)) { + LOG(ERROR) << "Failed to verify all blocks in care map file."; + return reboot_device(); + } } #else LOG(WARNING) << "dm-verity not enabled; marking without verification."; -- cgit v1.2.3 From 3e2345e1fe12e980484266d99d22f6b66c8ded4d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 9 Aug 2017 16:33:07 -0700 Subject: tests: Add the missing dependency on libhidlbase. It fails to build recovery_component_test with the following errors: out/soong/.intermediates/hardware/interfaces/boot/1.0/android.hardware.boot@1.0_genc++_headers/gen/android/hardware/boot/1.0/types.h:14: error: undefined reference to 'android::hardware::hidl_string::hidl_string(android::hardware::hidl_string const&)' out/soong/.intermediates/hardware/interfaces/boot/1.0/android.hardware.boot@1.0_genc++_headers/gen/android/hardware/boot/1.0/types.h:14: error: undefined reference to 'android::hardware::hidl_string::operator=(android::hardware::hidl_string const&)' out/soong/.intermediates/hardware/interfaces/boot/1.0/android.hardware.boot@1.0_genc++_headers/gen/android/hardware/boot/1.0/types.h:14: error: undefined reference to 'android::hardware::hidl_string::~hidl_string()' libupdate_verifier includes , which includes the 'types.h' above. In 'types.h', it defines struct CommandResult that's using android::hardware::hidl_string. Since libhidlbase doesn't have a static library target, remove 'LOCAL_FORCE_STATIC_EXECUTABLE := true', which isn't required for running tests. Test: mmma -j bootable/recovery Bug: 64538692 Change-Id: Iaa7c08adc241128d787274fcaea9b363e7ff93f4 (cherry picked from commit 102016ce1fe62190ace7016f2e7484b37f6391ea) --- tests/Android.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Android.mk b/tests/Android.mk index 8b1dc1099..f2497b8b3 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -111,7 +111,8 @@ LOCAL_SRC_FILES := \ component/update_verifier_test.cpp \ component/verifier_test.cpp -LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SHARED_LIBRARIES := \ + libhidlbase tune2fs_static_libraries := \ libext2_com_err \ -- cgit v1.2.3 From 5226f4715d6c961311249552f7e41d68ae2e80e6 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 2 Aug 2017 10:27:31 -0700 Subject: update_verifier: verify blocks in parallel This CL is to change update_verifier to verify blocks in parallel to maximize storage bandwidth, it also preallocate the buffer to avoid vector allocation within reading loop. Test: care_map.txt: system 16,0,517,556,32770,33084,98306,98620,163842,164156,229378,229692,294914,295228,483544,524288,524296 vendor 8,0,119,135,32770,32831,96150,98304,98306 With CL: init: Service 'update_verifier_nonencrypted' (pid 711) exited with status 0 waiting took 2.978424 seconds Without CL: init: Service 'update_verifier_nonencrypted' (pid 695) exited with status 0 waiting took 4.466320 seconds Bug: 63686531 Test: reboot with manual insert care_map.txt Change-Id: Idf791865f15f6ff6cad89bf7ff230ee46c6adccc (cherry picked from commit bd9664b5a01c8941949212973ca12be4df1b5d54) --- update_verifier/update_verifier.cpp | 82 +++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index 4c3cc46cf..faebbede0 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -123,11 +124,6 @@ static bool read_blocks(const std::string& partition, const std::string& range_s LOG(ERROR) << "Failed to find dm block device for " << partition; return false; } - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY))); - if (fd.get() == -1) { - PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition; - return false; - } // For block range string, first integer 'count' equals 2 * total number of valid ranges, // followed by 'count' number comma separated integers. Every two integers reprensent a @@ -142,37 +138,61 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } - size_t blk_count = 0; - for (size_t i = 1; i < ranges.size(); i += 2) { - unsigned int range_start, range_end; - bool parse_status = android::base::ParseUint(ranges[i], &range_start); - parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); - if (!parse_status || range_start >= range_end) { - LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1]; - return false; - } - - static constexpr size_t BLOCKSIZE = 4096; - if (lseek64(fd.get(), static_cast(range_start) * BLOCKSIZE, SEEK_SET) == -1) { - PLOG(ERROR) << "lseek to " << range_start << " failed"; - return false; - } + std::vector> threads; + size_t thread_num = std::thread::hardware_concurrency() ?: 4; + thread_num = std::min(thread_num, range_count / 2); + size_t group_range_count = range_count / thread_num; - size_t remain = (range_end - range_start) * BLOCKSIZE; - while (remain > 0) { - size_t to_read = std::min(remain, 1024 * BLOCKSIZE); - std::vector buf(to_read); - if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) { - PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end; + for (size_t t = 0; t < thread_num; t++) { + auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() { + size_t blk_count = 0; + static constexpr size_t kBlockSize = 4096; + std::vector buf(1024 * kBlockSize); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY))); + if (fd.get() == -1) { + PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition; return false; } - remain -= to_read; - } - blk_count += (range_end - range_start); + + for (size_t i = 1 + group_range_count * t; i < group_range_count * (t + 1) + 1; i += 2) { + unsigned int range_start, range_end; + bool parse_status = android::base::ParseUint(ranges[i], &range_start); + parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); + if (!parse_status || range_start >= range_end) { + LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1]; + return false; + } + + if (lseek64(fd.get(), static_cast(range_start) * kBlockSize, SEEK_SET) == -1) { + PLOG(ERROR) << "lseek to " << range_start << " failed"; + return false; + } + + size_t remain = (range_end - range_start) * kBlockSize; + while (remain > 0) { + size_t to_read = std::min(remain, 1024 * kBlockSize); + if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) { + PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end; + return false; + } + remain -= to_read; + } + blk_count += (range_end - range_start); + } + LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device; + return true; + }; + + threads.emplace_back(std::async(std::launch::async, thread_func)); } - LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device; - return true; + bool ret = true; + for (auto& t : threads) { + ret = t.get() && ret; + } + LOG(INFO) << "Finished reading blocks on " << dm_block_device << " with " << thread_num + << " threads."; + return ret; } // Returns true to indicate a passing verification (or the error should be ignored); Otherwise -- cgit v1.2.3 From 0470ceea381775b09eee931858c3320be88cc637 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 2 Aug 2017 17:11:04 -0700 Subject: Allow customizing WearRecoveryUI via Makefile variables. With the following Makefile variables, we can reduce the work of writing (copy/pasting) device-specific WearRecoveryUI classes. The list of Makefile variables (the ones useful for Wear devices): - TARGET_RECOVERY_UI_MARGIN_HEIGHT (default: 0) - TARGET_RECOVERY_UI_MARGIN_WIDTH (default: 0) Specify the margin space that we don't want to display texts. They replace the former outer_width and outer_height. - TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD (default: 50) - TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD (default: 90) Specify the sensitivity of recognizing a swipe. Devices give absolute positions, so for some devices we need to adjust the thresholds. - TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE Specify the progress bar vertical position, which should be adjusted to the actual height of a device. It replaces the former progress_bar_y. - TARGET_RECOVERY_UI_ANIMATION_FPS (default: 30) Specify the animation FPS if using device-specific animation images. It replaces the former animation_fps. Devices can specify "TARGET_RECOVERY_UI_LIB := librecovery_ui_wear", with optionally defined Makefile vars above, in BoardConfig.mk to customize their WearRecoveryUI. Also remove the obsolete wear_touch.{cpp,h}, which has been merged into ui.cpp in commit 5f8dd9951d986b65d98d6a9ea38003427e9e46df. Bug: 64307776 Test: Change the device BoardConfig.mk and test recovery image. Change-Id: Id0fb2d4e3977ab5ddd31e71f9535470cab70e41b --- Android.mk | 23 +++++++- screen_ui.cpp | 4 +- screen_ui.h | 6 +- wear_device.cpp | 23 ++++++++ wear_touch.cpp | 177 -------------------------------------------------------- wear_touch.h | 58 ------------------- wear_ui.cpp | 21 ++++--- wear_ui.h | 5 +- 8 files changed, 64 insertions(+), 253 deletions(-) create mode 100644 wear_device.cpp delete mode 100644 wear_touch.cpp delete mode 100644 wear_touch.h diff --git a/Android.mk b/Android.mk index 967b9dfbe..b1ee2440b 100644 --- a/Android.mk +++ b/Android.mk @@ -79,7 +79,6 @@ LOCAL_SRC_FILES := \ ui.cpp \ vr_ui.cpp \ wear_ui.cpp \ - wear_touch.cpp \ LOCAL_MODULE := recovery @@ -120,6 +119,18 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=90 endif +ifneq ($(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE),) +LOCAL_CFLAGS += -DRECOVERY_UI_PROGRESS_BAR_BASELINE=$(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE) +else +LOCAL_CFLAGS += -DRECOVERY_UI_PROGRESS_BAR_BASELINE=259 +endif + +ifneq ($(TARGET_RECOVERY_UI_ANIMATION_FPS),) +LOCAL_CFLAGS += -DRECOVERY_UI_ANIMATION_FPS=$(TARGET_RECOVERY_UI_ANIMATION_FPS) +else +LOCAL_CFLAGS += -DRECOVERY_UI_ANIMATION_FPS=30 +endif + ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) else @@ -216,6 +227,16 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) +# Wear default device +# =============================== +include $(CLEAR_VARS) +LOCAL_SRC_FILES := wear_device.cpp + +# Should match TARGET_RECOVERY_UI_LIB in BoardConfig.mk. +LOCAL_MODULE := librecovery_ui_wear + +include $(BUILD_STATIC_LIBRARY) + # vr headset default device # =============================== include $(CLEAR_VARS) diff --git a/screen_ui.cpp b/screen_ui.cpp index 8f792f162..c83a7659e 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -53,6 +53,7 @@ static double now() { ScreenRecoveryUI::ScreenRecoveryUI() : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH), kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT), + kAnimationFps(RECOVERY_UI_ANIMATION_FPS), density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), currentIcon(NONE), progressBarType(EMPTY), @@ -77,7 +78,6 @@ ScreenRecoveryUI::ScreenRecoveryUI() loop_frames(0), current_frame(0), intro_done(false), - animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), max_stage(-1), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} @@ -375,7 +375,7 @@ void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) { } void ScreenRecoveryUI::ProgressThreadLoop() { - double interval = 1.0 / animation_fps; + double interval = 1.0 / kAnimationFps; while (true) { double start = now(); pthread_mutex_lock(&updateMutex); diff --git a/screen_ui.h b/screen_ui.h index 8402fac00..1f40164af 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -84,6 +84,9 @@ class ScreenRecoveryUI : public RecoveryUI { const int kMarginWidth; const int kMarginHeight; + // Number of frames per sec (default: 30) for both parts of the animation. + const int kAnimationFps; + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. const float density_; @@ -141,9 +144,6 @@ class ScreenRecoveryUI : public RecoveryUI { size_t current_frame; bool intro_done; - // Number of frames per sec (default: 30) for both parts of the animation. - int animation_fps; - int stage, max_stage; int char_width_; diff --git a/wear_device.cpp b/wear_device.cpp new file mode 100644 index 000000000..3268130b0 --- /dev/null +++ b/wear_device.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device.h" +#include "wear_ui.h" + +Device* make_device() { + return new Device(new WearRecoveryUI); +} + diff --git a/wear_touch.cpp b/wear_touch.cpp deleted file mode 100644 index e2ab44d2d..000000000 --- a/wear_touch.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "wear_touch.h" - -#define DEVICE_PATH "/dev/input" - -WearSwipeDetector::WearSwipeDetector(int low, int high, OnSwipeCallback callback, void* cookie): - mLowThreshold(low), - mHighThreshold(high), - mCallback(callback), - mCookie(cookie), - mCurrentSlot(-1) { - pthread_create(&mThread, NULL, touch_thread, this); -} - -WearSwipeDetector::~WearSwipeDetector() { -} - -void WearSwipeDetector::detect(int dx, int dy) { - enum SwipeDirection direction; - - if (abs(dy) < mLowThreshold && abs(dx) > mHighThreshold) { - direction = dx < 0 ? LEFT : RIGHT; - } else if (abs(dx) < mLowThreshold && abs(dy) > mHighThreshold) { - direction = dy < 0 ? UP : DOWN; - } else { - LOG(DEBUG) << "Ignore " << dx << " " << dy; - return; - } - - LOG(DEBUG) << "Swipe direction=" << direction; - mCallback(mCookie, direction); -} - -void WearSwipeDetector::process(struct input_event *event) { - if (mCurrentSlot < 0) { - mCallback(mCookie, UP); - mCurrentSlot = 0; - } - - if (event->type == EV_ABS) { - if (event->code == ABS_MT_SLOT) - mCurrentSlot = event->value; - - // Ignore other fingers - if (mCurrentSlot > 0) { - return; - } - - switch (event->code) { - case ABS_MT_POSITION_X: - mX = event->value; - mFingerDown = true; - break; - - case ABS_MT_POSITION_Y: - mY = event->value; - mFingerDown = true; - break; - - case ABS_MT_TRACKING_ID: - if (event->value < 0) - mFingerDown = false; - break; - } - } else if (event->type == EV_SYN) { - if (event->code == SYN_REPORT) { - if (mFingerDown && !mSwiping) { - mStartX = mX; - mStartY = mY; - mSwiping = true; - } else if (!mFingerDown && mSwiping) { - mSwiping = false; - detect(mX - mStartX, mY - mStartY); - } - } - } -} - -void WearSwipeDetector::run() { - int fd = findDevice(DEVICE_PATH); - if (fd < 0) { - LOG(ERROR) << "no input devices found"; - return; - } - - struct input_event event; - while (read(fd, &event, sizeof(event)) == sizeof(event)) { - process(&event); - } - - close(fd); -} - -void* WearSwipeDetector::touch_thread(void* cookie) { - (static_cast(cookie))->run(); - return NULL; -} - -#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8))) - -int WearSwipeDetector::openDevice(const char *device) { - int fd = open(device, O_RDONLY); - if (fd < 0) { - PLOG(ERROR) << "could not open " << device; - return false; - } - - char name[80]; - name[sizeof(name) - 1] = '\0'; - if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { - PLOG(ERROR) << "could not get device name for " << device; - name[0] = '\0'; - } - - uint8_t bits[512]; - memset(bits, 0, sizeof(bits)); - int ret = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits); - if (ret > 0) { - if (test_bit(ABS_MT_POSITION_X, bits) && test_bit(ABS_MT_POSITION_Y, bits)) { - LOG(DEBUG) << "Found " << device << " " << name; - return fd; - } - } - - close(fd); - return -1; -} - -int WearSwipeDetector::findDevice(const char* path) { - DIR* dir = opendir(path); - if (dir == NULL) { - PLOG(ERROR) << "Could not open directory " << path; - return false; - } - - struct dirent* entry; - int ret = -1; - while (ret < 0 && (entry = readdir(dir)) != NULL) { - if (entry->d_name[0] == '.') continue; - - char device[PATH_MAX]; - device[PATH_MAX-1] = '\0'; - snprintf(device, PATH_MAX-1, "%s/%s", path, entry->d_name); - - ret = openDevice(device); - } - - closedir(dir); - return ret; -} - diff --git a/wear_touch.h b/wear_touch.h deleted file mode 100644 index 9a1d3150c..000000000 --- a/wear_touch.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __WEAR_TOUCH_H -#define __WEAR_TOUCH_H - -#include - -class WearSwipeDetector { - -public: - enum SwipeDirection { UP, DOWN, RIGHT, LEFT }; - typedef void (*OnSwipeCallback)(void* cookie, enum SwipeDirection direction); - - WearSwipeDetector(int low, int high, OnSwipeCallback cb, void* cookie); - ~WearSwipeDetector(); - -private: - void run(); - void process(struct input_event *event); - void detect(int dx, int dy); - - pthread_t mThread; - static void* touch_thread(void* cookie); - - int findDevice(const char* path); - int openDevice(const char* device); - - int mLowThreshold; - int mHighThreshold; - - OnSwipeCallback mCallback; - void *mCookie; - - int mX; - int mY; - int mStartX; - int mStartY; - - int mCurrentSlot; - bool mFingerDown; - bool mSwiping; -}; - -#endif // __WEAR_TOUCH_H diff --git a/wear_ui.cpp b/wear_ui.cpp index 18c30d34a..b8801a0b3 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -51,10 +51,15 @@ static double now() { } WearRecoveryUI::WearRecoveryUI() - : progress_bar_y(259), outer_height(0), outer_width(0), menu_unusable_rows(0) { + : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), menu_unusable_rows(9) { + // TODO: menu_unusable_rows should be computed based on the lines in draw_screen_locked(). + + // TODO: The following three variables are likely not needed. The first two are detected + // automatically in ScreenRecoveryUI::LoadAnimation(), based on the actual files seen on device. intro_frames = 22; loop_frames = 60; - animation_fps = 30; + + touch_screen_allowed_ = true; for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL; @@ -62,7 +67,7 @@ WearRecoveryUI::WearRecoveryUI() } int WearRecoveryUI::GetProgressBaseline() const { - return progress_bar_y; + return kProgressBarBaseline; } // Draw background frame on the screen. Does not flip pages. @@ -113,8 +118,8 @@ void WearRecoveryUI::draw_screen_locked() { SetColor(TEXT_FILL); gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - int y = outer_height; - int x = outer_width; + int y = kMarginHeight; + int x = kMarginWidth; if (show_menu) { std::string recovery_fingerprint = android::base::GetProperty("ro.bootimage.build.fingerprint", ""); @@ -170,7 +175,7 @@ void WearRecoveryUI::draw_screen_locked() { int ty; int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - char_height_ - outer_height; ty > y + 2 && count < text_rows_; + for (int ty = gr_fb_height() - char_height_ - kMarginHeight; ty > y + 2 && count < text_rows_; ty -= char_height_, ++count) { gr_text(gr_sys_font(), x + 4, ty, text_[row], 0); --row; @@ -190,12 +195,12 @@ bool WearRecoveryUI::InitTextParams() { return false; } - text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_; + text_cols_ = (gr_fb_width() - (kMarginWidth * 2)) / char_width_; if (text_rows_ > kMaxRows) text_rows_ = kMaxRows; if (text_cols_ > kMaxCols) text_cols_ = kMaxCols; - visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_; + visible_text_rows = (gr_fb_height() - (kMarginHeight * 2)) / char_height_; return true; } diff --git a/wear_ui.h b/wear_ui.h index a814118c7..c2bbdb66e 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -42,10 +42,7 @@ class WearRecoveryUI : public ScreenRecoveryUI { protected: // progress bar vertical position, it's centered horizontally - int progress_bar_y; - - // outer of window - int outer_height, outer_width; + const int kProgressBarBaseline; // Unusable rows when displaying the recovery menu, including the lines for headers (Android // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen. -- cgit v1.2.3 From eea3af3f911d36ac1a82a9fb95d24912cc07e3b1 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 11 Aug 2017 13:50:24 -0700 Subject: wear_ui: Expose menu_unusable_rows via Makefile var. This variable is useful on small screens (e.g. on watches) to handle long menus. We should have better way to handle this value smartly. Prior to that, expose the value to be overridable by using the generic wearable UI module (librecovery_ui_wear). Bug: 64307776 Test: Define the variable, build and boot into recovery image and check the UI menu. Change-Id: I5d7a6baa8bb4cc852bfcc2a7b3cc9686c1c8817e --- Android.mk | 6 ++++++ wear_ui.cpp | 7 ++++--- wear_ui.h | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index b1ee2440b..776e6ea19 100644 --- a/Android.mk +++ b/Android.mk @@ -131,6 +131,12 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_ANIMATION_FPS=30 endif +ifneq ($(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS),) +LOCAL_CFLAGS += -DRECOVERY_UI_MENU_UNUSABLE_ROWS=$(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS) +else +LOCAL_CFLAGS += -DRECOVERY_UI_MENU_UNUSABLE_ROWS=9 +endif + ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) else diff --git a/wear_ui.cpp b/wear_ui.cpp index b8801a0b3..169ef20e1 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -51,8 +51,9 @@ static double now() { } WearRecoveryUI::WearRecoveryUI() - : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), menu_unusable_rows(9) { - // TODO: menu_unusable_rows should be computed based on the lines in draw_screen_locked(). + : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), + kMenuUnusableRows(RECOVERY_UI_MENU_UNUSABLE_ROWS) { + // TODO: kMenuUnusableRows should be computed based on the lines in draw_screen_locked(). // TODO: The following three variables are likely not needed. The first two are detected // automatically in ScreenRecoveryUI::LoadAnimation(), based on the actual files seen on device. @@ -268,7 +269,7 @@ void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* it show_menu = true; menu_sel = initial_selection; menu_start = 0; - menu_end = visible_text_rows - 1 - menu_unusable_rows; + menu_end = visible_text_rows - 1 - kMenuUnusableRows; if (menu_items <= menu_end) menu_end = menu_items; update_screen_locked(); } diff --git a/wear_ui.h b/wear_ui.h index c2bbdb66e..3bd90b699 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -46,7 +46,7 @@ class WearRecoveryUI : public ScreenRecoveryUI { // Unusable rows when displaying the recovery menu, including the lines for headers (Android // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen. - int menu_unusable_rows; + const int kMenuUnusableRows; int GetProgressBaseline() const override; -- cgit v1.2.3 From 616256e7398db7432822dde42d0f4e343961f934 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 12 Aug 2017 06:53:13 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I99a2cf060c75a70b3baf3c22d538fbfa08828474 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- tools/recovery_l10n/res/values-az/strings.xml | 9 --------- tools/recovery_l10n/res/values-b+sr+Latn/strings.xml | 9 --------- tools/recovery_l10n/res/values-be/strings.xml | 9 --------- tools/recovery_l10n/res/values-bs/strings.xml | 9 --------- tools/recovery_l10n/res/values-en-rAU/strings.xml | 9 --------- tools/recovery_l10n/res/values-gu/strings.xml | 9 --------- tools/recovery_l10n/res/values-kk/strings.xml | 9 --------- tools/recovery_l10n/res/values-kn/strings.xml | 9 --------- tools/recovery_l10n/res/values-mr/strings.xml | 4 ++-- tools/recovery_l10n/res/values-pa/strings.xml | 9 --------- tools/recovery_l10n/res/values-pt-rBR/strings.xml | 9 --------- tools/recovery_l10n/res/values-sq/strings.xml | 9 --------- tools/recovery_l10n/res/values-te/strings.xml | 9 --------- tools/recovery_l10n/res/values-ur/strings.xml | 9 --------- tools/recovery_l10n/res/values-uz/strings.xml | 9 --------- 15 files changed, 2 insertions(+), 128 deletions(-) delete mode 100644 tools/recovery_l10n/res/values-az/strings.xml delete mode 100644 tools/recovery_l10n/res/values-b+sr+Latn/strings.xml delete mode 100644 tools/recovery_l10n/res/values-be/strings.xml delete mode 100644 tools/recovery_l10n/res/values-bs/strings.xml delete mode 100644 tools/recovery_l10n/res/values-en-rAU/strings.xml delete mode 100644 tools/recovery_l10n/res/values-gu/strings.xml delete mode 100644 tools/recovery_l10n/res/values-kk/strings.xml delete mode 100644 tools/recovery_l10n/res/values-kn/strings.xml delete mode 100644 tools/recovery_l10n/res/values-pa/strings.xml delete mode 100644 tools/recovery_l10n/res/values-pt-rBR/strings.xml delete mode 100644 tools/recovery_l10n/res/values-sq/strings.xml delete mode 100644 tools/recovery_l10n/res/values-te/strings.xml delete mode 100644 tools/recovery_l10n/res/values-ur/strings.xml delete mode 100644 tools/recovery_l10n/res/values-uz/strings.xml diff --git a/tools/recovery_l10n/res/values-az/strings.xml b/tools/recovery_l10n/res/values-az/strings.xml deleted file mode 100644 index c6765a9ea..000000000 --- a/tools/recovery_l10n/res/values-az/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Sistem güncəlləməsi quraşdırılır..." - "Silinir" - "Əmr yoxdur" - "Xəta!" - "Təhlükəsizlik güncəlləməsi yüklənir" - diff --git a/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml deleted file mode 100644 index c2d8f2239..000000000 --- a/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Ažuriranje sistema se instalira" - "Briše se" - "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 deleted file mode 100644 index 7c0954d31..000000000 --- a/tools/recovery_l10n/res/values-be/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Усталёўка абнаўлення сістэмы" - "Сціранне" - "Няма каманды" - "Памылка" - "Усталёўка абнаўлення сістэмы бяспекі" - diff --git a/tools/recovery_l10n/res/values-bs/strings.xml b/tools/recovery_l10n/res/values-bs/strings.xml deleted file mode 100644 index 412cf0276..000000000 --- a/tools/recovery_l10n/res/values-bs/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Ažuriranje sistema…" - "Brisanje u toku" - "Nema komande" - "Greška!" - "Instaliranje sigurnosnog ažuriranja…" - diff --git a/tools/recovery_l10n/res/values-en-rAU/strings.xml b/tools/recovery_l10n/res/values-en-rAU/strings.xml deleted file mode 100644 index dc75c2374..000000000 --- a/tools/recovery_l10n/res/values-en-rAU/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Installing system update" - "Erasing" - "No command" - "Error!" - "Installing security update" - diff --git a/tools/recovery_l10n/res/values-gu/strings.xml b/tools/recovery_l10n/res/values-gu/strings.xml deleted file mode 100644 index 2355a0f4f..000000000 --- a/tools/recovery_l10n/res/values-gu/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "સિસ્ટમ અપડેટ ઇન્સ્ટૉલ કરી રહ્યાં છે" - "કાઢી નાખી રહ્યું છે" - "કોઈ આદેશ નથી" - "ભૂલ!" - "સુરક્ષા અપડેટ ઇન્સ્ટૉલ કરી રહ્યાં છે" - diff --git a/tools/recovery_l10n/res/values-kk/strings.xml b/tools/recovery_l10n/res/values-kk/strings.xml deleted file mode 100644 index a4bd86e66..000000000 --- a/tools/recovery_l10n/res/values-kk/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Жүйе жаңартуы орнатылуда" - "Өшірілуде" - "Пәрмен жоқ" - "Қате!" - "Қауіпсіздік жаңартуы орнатылуда" - diff --git a/tools/recovery_l10n/res/values-kn/strings.xml b/tools/recovery_l10n/res/values-kn/strings.xml deleted file mode 100644 index 5bf6260ee..000000000 --- a/tools/recovery_l10n/res/values-kn/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "ಸಿಸ್ಟಂ ಅಪ್‌ಡೇಟ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" - "ಅಳಿಸಲಾಗುತ್ತಿದೆ" - "ಯಾವುದೇ ಆದೇಶವಿಲ್ಲ" - "ದೋಷ!" - "ಭದ್ರತೆಯ ಅಪ್‌ಡೇಟ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" - diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml index 8cf86f773..017a515c0 100644 --- a/tools/recovery_l10n/res/values-mr/strings.xml +++ b/tools/recovery_l10n/res/values-mr/strings.xml @@ -1,9 +1,9 @@ - "सिस्टम अद्यतन स्थापित करीत आहे" + "सिस्टम अपडेट इंस्टॉल करत आहे" "मिटवत आहे" "कोणताही आदेश नाही" "त्रुटी!" - "सुरक्षा अद्यतन स्थापित करीत आहे" + "सुरक्षा अपडेट इंस्टॉल करत आहे" diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml deleted file mode 100644 index 8564c9c36..000000000 --- a/tools/recovery_l10n/res/values-pa/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" - "ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ" - "ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ" - "ਅਸ਼ੁੱਧੀ!" - "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" - diff --git a/tools/recovery_l10n/res/values-pt-rBR/strings.xml b/tools/recovery_l10n/res/values-pt-rBR/strings.xml deleted file mode 100644 index b72704385..000000000 --- a/tools/recovery_l10n/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Instalando atualização do sistema" - "Apagando" - "Nenhum comando" - "Erro!" - "Instalando atualização de segurança" - diff --git a/tools/recovery_l10n/res/values-sq/strings.xml b/tools/recovery_l10n/res/values-sq/strings.xml deleted file mode 100644 index 1156931fb..000000000 --- a/tools/recovery_l10n/res/values-sq/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Po instalon përditësimin e sistemit" - "Po spastron" - "Nuk ka komanda" - "Gabim!" - "Po instalon përditësimin e sigurisë" - diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml deleted file mode 100644 index cfb02c915..000000000 --- a/tools/recovery_l10n/res/values-te/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "సిస్టమ్ నవీకరణను ఇన్‍స్టాల్ చేస్తోంది" - "డేటాను తొలగిస్తోంది" - "ఆదేశం లేదు" - "లోపం సంభవించింది!" - "భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది" - diff --git a/tools/recovery_l10n/res/values-ur/strings.xml b/tools/recovery_l10n/res/values-ur/strings.xml deleted file mode 100644 index 12e32fbc1..000000000 --- a/tools/recovery_l10n/res/values-ur/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "سسٹم اپ ڈیٹ انسٹال ہو رہی ہے" - "صاف ہو رہا ہے" - "کوئی کمانڈ نہیں ہے" - "خرابی!" - "سیکیورٹی اپ ڈیٹ انسٹال ہو رہی ہے" - diff --git a/tools/recovery_l10n/res/values-uz/strings.xml b/tools/recovery_l10n/res/values-uz/strings.xml deleted file mode 100644 index 2c309d646..000000000 --- a/tools/recovery_l10n/res/values-uz/strings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - "Tizim yangilanishi o‘rnatilmoqda" - "Tozalanmoqda…" - "Buyruq yo‘q" - "Xato!" - "Xavfsizlik yangilanishi o‘rnatilmoqda" - -- cgit v1.2.3 From d82a2ed50bab5ea014bcf3b1e5541e21829b4ecb Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 8 Aug 2017 17:35:01 -0700 Subject: Add a new PatchChunk class in imgdiff This way we can keep the input images const when calling genetatepatches(). Test: recovery component test; diff and patch on chrome.apk; generate recovery-from-boot.p for angler. Change-Id: I65b5689b88f6719c6ede46bb82def0c4caeb8a61 --- applypatch/imgdiff.cpp | 528 +++++++++++++++++++++++++++---------------------- 1 file changed, 292 insertions(+), 236 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 880265260..a81e385a3 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -168,15 +168,14 @@ class ImageChunk { static constexpr auto METHOD = Z_DEFLATED; static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY; - ImageChunk(int type, size_t start, const std::vector* file_content, size_t raw_data_len) + ImageChunk(int type, size_t start, const std::vector* file_content, size_t raw_data_len, + std::string entry_name = {}) : type_(type), start_(start), input_file_ptr_(file_content), raw_data_len_(raw_data_len), compress_level_(6), - source_start_(0), - source_len_(0), - source_uncompressed_len_(0) { + entry_name_(std::move(entry_name)) { CHECK(file_content != nullptr) << "input file container can't be nullptr"; } @@ -189,6 +188,12 @@ class ImageChunk { const std::string& GetEntryName() const { return entry_name_; } + size_t GetStartOffset() const { + return start_; + } + int GetCompressLevel() const { + return compress_level_; + } // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return // the raw data. @@ -200,8 +205,6 @@ class ImageChunk { entry_name_.c_str()); } - void SetSourceInfo(const ImageChunk& other); - void SetEntryName(std::string entryname); void SetUncompressedData(std::vector data); bool SetBonusData(const std::vector& bonus_data); @@ -210,57 +213,46 @@ class ImageChunk { return !(*this == other); } - size_t GetHeaderSize(size_t patch_size) const; - // Return the offset of the next patch into the patch data. - size_t WriteHeaderToFd(int fd, const std::vector& patch, size_t offset) const; - /* - * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob - * of uninterpreted data). The resulting patch will likely be about - * as big as the target file, but it lets us handle the case of images - * where some gzip chunks are reconstructible but others aren't (by - * treating the ones that aren't as normal chunks). + * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob of uninterpreted data). + * The resulting patch will likely be about as big as the target file, but it lets us handle + * the case of images where some gzip chunks are reconstructible but others aren't (by treating + * the ones that aren't as normal chunks). */ void ChangeDeflateChunkToNormal(); - bool ChangeChunkToRaw(size_t patch_size); /* - * Verify that we can reproduce exactly the same compressed data that - * we started with. Sets the level, method, windowBits, memLevel, and - * strategy fields in the chunk to the encoding parameters needed to - * produce the right output. + * Verify that we can reproduce exactly the same compressed data that we started with. Sets the + * level, method, windowBits, memLevel, and strategy fields in the chunk to the encoding + * parameters needed to produce the right output. */ bool ReconstructDeflateChunk(); bool IsAdjacentNormal(const ImageChunk& other) const; void MergeAdjacentNormal(const ImageChunk& other); /* - * Compute a bsdiff patch between |this| and the input source chunks. - * Store the result in the patch_data. + * Compute a bsdiff patch between |src| and |tgt|; Store the result in the patch_data. * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk is used * repeatedly, pass nullptr if not needed. */ - bool MakePatch(const ImageChunk& src, std::vector* patch_data, saidx_t** bsdiff_cache); + static bool MakePatch(const ImageChunk& tgt, const ImageChunk& src, + std::vector* patch_data, saidx_t** bsdiff_cache); private: + const uint8_t* GetRawData() const; + bool TryReconstruction(int level); + int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW size_t start_; // offset of chunk in the original input file const std::vector* input_file_ptr_; // ptr to the full content of original input file size_t raw_data_len_; - // --- for CHUNK_DEFLATE chunks only: --- - std::vector uncompressed_data_; - std::string entry_name_; // used for zip entries - // deflate encoder parameters int compress_level_; - size_t source_start_; - size_t source_len_; - size_t source_uncompressed_len_; - - const uint8_t* GetRawData() const; - bool TryReconstruction(int level); + // --- for CHUNK_DEFLATE chunks only: --- + std::vector uncompressed_data_; + std::string entry_name_; // used for zip entries }; const uint8_t* ImageChunk::GetRawData() const { @@ -290,20 +282,6 @@ bool ImageChunk::operator==(const ImageChunk& other) const { memcmp(GetRawData(), other.GetRawData(), raw_data_len_) == 0); } -void ImageChunk::SetSourceInfo(const ImageChunk& src) { - source_start_ = src.start_; - if (type_ == CHUNK_NORMAL) { - source_len_ = src.raw_data_len_; - } else if (type_ == CHUNK_DEFLATE) { - source_len_ = src.raw_data_len_; - source_uncompressed_len_ = src.uncompressed_data_.size(); - } -} - -void ImageChunk::SetEntryName(std::string entryname) { - entry_name_ = std::move(entryname); -} - void ImageChunk::SetUncompressedData(std::vector data) { uncompressed_data_ = std::move(data); } @@ -316,18 +294,6 @@ bool ImageChunk::SetBonusData(const std::vector& bonus_data) { return true; } -// Convert CHUNK_NORMAL & CHUNK_DEFLATE to CHUNK_RAW if the target size is -// smaller. Also take the header size into account during size comparison. -bool ImageChunk::ChangeChunkToRaw(size_t patch_size) { - if (type_ == CHUNK_RAW) { - return true; - } else if (type_ == CHUNK_NORMAL && (raw_data_len_ <= 160 || raw_data_len_ < patch_size)) { - type_ = CHUNK_RAW; - return true; - } - return false; -} - void ImageChunk::ChangeDeflateChunkToNormal() { if (type_ != CHUNK_DEFLATE) return; type_ = CHUNK_NORMAL; @@ -335,61 +301,6 @@ void ImageChunk::ChangeDeflateChunkToNormal() { uncompressed_data_.clear(); } -// Header size: -// header_type 4 bytes -// CHUNK_NORMAL 8*3 = 24 bytes -// CHUNK_DEFLATE 8*5 + 4*5 = 60 bytes -// CHUNK_RAW 4 bytes + patch_size -size_t ImageChunk::GetHeaderSize(size_t patch_size) const { - switch (type_) { - case CHUNK_NORMAL: - return 4 + 8 * 3; - case CHUNK_DEFLATE: - return 4 + 8 * 5 + 4 * 5; - case CHUNK_RAW: - return 4 + 4 + patch_size; - default: - CHECK(false) << "unexpected chunk type: " << type_; // Should not reach here. - return 0; - } -} - -size_t ImageChunk::WriteHeaderToFd(int fd, const std::vector& patch, size_t offset) const { - Write4(fd, type_); - switch (type_) { - case CHUNK_NORMAL: - printf("normal (%10zu, %10zu) %10zu\n", start_, raw_data_len_, patch.size()); - Write8(fd, static_cast(source_start_)); - Write8(fd, static_cast(source_len_)); - Write8(fd, static_cast(offset)); - return offset + patch.size(); - case CHUNK_DEFLATE: - printf("deflate (%10zu, %10zu) %10zu %s\n", start_, raw_data_len_, patch.size(), - entry_name_.c_str()); - Write8(fd, static_cast(source_start_)); - Write8(fd, static_cast(source_len_)); - Write8(fd, static_cast(offset)); - Write8(fd, static_cast(source_uncompressed_len_)); - Write8(fd, static_cast(uncompressed_data_.size())); - Write4(fd, compress_level_); - Write4(fd, METHOD); - Write4(fd, WINDOWBITS); - Write4(fd, MEMLEVEL); - Write4(fd, STRATEGY); - return offset + patch.size(); - case CHUNK_RAW: - printf("raw (%10zu, %10zu)\n", start_, raw_data_len_); - Write4(fd, static_cast(patch.size())); - if (!android::base::WriteFully(fd, patch.data(), patch.size())) { - CHECK(false) << "failed to write " << patch.size() <<" bytes patch"; - } - return offset; - default: - CHECK(false) << "unexpected chunk type: " << type_; - return offset; - } -} - bool ImageChunk::IsAdjacentNormal(const ImageChunk& other) const { if (type_ != CHUNK_NORMAL || other.type_ != CHUNK_NORMAL) { return false; @@ -402,15 +313,8 @@ void ImageChunk::MergeAdjacentNormal(const ImageChunk& other) { raw_data_len_ = raw_data_len_ + other.raw_data_len_; } -bool ImageChunk::MakePatch(const ImageChunk& src, std::vector* patch_data, - saidx_t** bsdiff_cache) { - if (ChangeChunkToRaw(0)) { - size_t patch_size = DataLengthForPatch(); - patch_data->resize(patch_size); - std::copy(DataForPatch(), DataForPatch() + patch_size, patch_data->begin()); - return true; - } - +bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src, + std::vector* patch_data, saidx_t** bsdiff_cache) { #if defined(__ANDROID__) char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX"; #else @@ -424,8 +328,8 @@ bool ImageChunk::MakePatch(const ImageChunk& src, std::vector* patch_da } close(fd); - int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), DataForPatch(), - DataLengthForPatch(), ptemp, bsdiff_cache); + int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), tgt.DataForPatch(), + tgt.DataLengthForPatch(), ptemp, bsdiff_cache); if (r != 0) { printf("bsdiff() failed: %d\n", r); return false; @@ -443,14 +347,7 @@ bool ImageChunk::MakePatch(const ImageChunk& src, std::vector* patch_da } size_t sz = static_cast(st.st_size); - // Change the chunk type to raw if the patch takes less space that way. - if (ChangeChunkToRaw(sz)) { - unlink(ptemp); - size_t patch_size = DataLengthForPatch(); - patch_data->resize(patch_size); - std::copy(DataForPatch(), DataForPatch() + patch_size, patch_data->begin()); - return true; - } + patch_data->resize(sz); if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) { printf("failed to read \"%s\" %s\n", ptemp, strerror(errno)); @@ -459,7 +356,6 @@ bool ImageChunk::MakePatch(const ImageChunk& src, std::vector* patch_da } unlink(ptemp); - SetSourceInfo(src); return true; } @@ -470,8 +366,8 @@ bool ImageChunk::ReconstructDeflateChunk() { return false; } - // We only check two combinations of encoder parameters: level 6 - // (the default) and level 9 (the maximum). + // We only check two combinations of encoder parameters: level 6 (the default) and level 9 + // (the maximum). for (int level = 6; level <= 9; level += 3) { if (TryReconstruction(level)) { compress_level_ = level; @@ -483,10 +379,9 @@ bool ImageChunk::ReconstructDeflateChunk() { } /* - * Takes the uncompressed data stored in the chunk, compresses it - * using the zlib parameters stored in the chunk, and checks that it - * matches exactly the compressed data we started with (also stored in - * the chunk). + * Takes the uncompressed data stored in the chunk, compresses it using the zlib parameters stored + * in the chunk, and checks that it matches exactly the compressed data we started with (also + * stored in the chunk). */ bool ImageChunk::TryReconstruction(int level) { z_stream strm; @@ -529,6 +424,156 @@ bool ImageChunk::TryReconstruction(int level) { return true; } +// PatchChunk stores the patch data between a source chunk and a target chunk. It also keeps track +// of the metadata of src&tgt chunks (e.g. offset, raw data length, uncompressed data length). +class PatchChunk { + public: + PatchChunk(const ImageChunk& tgt, const ImageChunk& src, std::vector data) + : type_(tgt.GetType()), + source_start_(src.GetStartOffset()), + source_len_(src.GetRawDataLength()), + source_uncompressed_len_(src.DataLengthForPatch()), + target_start_(tgt.GetStartOffset()), + target_len_(tgt.GetRawDataLength()), + target_uncompressed_len_(tgt.DataLengthForPatch()), + target_compress_level_(tgt.GetCompressLevel()), + data_(std::move(data)) {} + + // Construct a CHUNK_RAW patch from the target data directly. + explicit PatchChunk(const ImageChunk& tgt) + : type_(CHUNK_RAW), + source_start_(0), + source_len_(0), + source_uncompressed_len_(0), + target_start_(tgt.GetStartOffset()), + target_len_(tgt.GetRawDataLength()), + target_uncompressed_len_(tgt.DataLengthForPatch()), + target_compress_level_(tgt.GetCompressLevel()), + data_(tgt.DataForPatch(), tgt.DataForPatch() + tgt.DataLengthForPatch()) {} + + // Return true if raw data size is smaller than the patch size. + static bool RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size); + + static bool WritePatchDataToFd(const std::vector& patch_chunks, int patch_fd); + + private: + size_t GetHeaderSize() const; + size_t WriteHeaderToFd(int fd, size_t offset) const; + + // The patch chunk type is the same as the target chunk type. The only exception is we change + // the |type_| to CHUNK_RAW if target length is smaller than the patch size. + int type_; + + size_t source_start_; + size_t source_len_; + size_t source_uncompressed_len_; + + size_t target_start_; // offset of the target chunk within the target file + size_t target_len_; + size_t target_uncompressed_len_; + size_t target_compress_level_; // the deflate compression level of the target chunk. + + std::vector data_; // storage for the patch data +}; + +// Return true if raw data is smaller than the patch size. +bool PatchChunk::RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size) { + size_t target_len = tgt.GetRawDataLength(); + return (tgt.GetType() == CHUNK_NORMAL && (target_len <= 160 || target_len < patch_size)); +} + +// Header size: +// header_type 4 bytes +// CHUNK_NORMAL 8*3 = 24 bytes +// CHUNK_DEFLATE 8*5 + 4*5 = 60 bytes +// CHUNK_RAW 4 bytes + patch_size +size_t PatchChunk::GetHeaderSize() const { + switch (type_) { + case CHUNK_NORMAL: + return 4 + 8 * 3; + case CHUNK_DEFLATE: + return 4 + 8 * 5 + 4 * 5; + case CHUNK_RAW: + return 4 + 4 + data_.size(); + default: + CHECK(false) << "unexpected chunk type: " << type_; // Should not reach here. + return 0; + } +} + +// Return the offset of the next patch into the patch data. +size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset) const { + Write4(fd, type_); + switch (type_) { + case CHUNK_NORMAL: + printf("normal (%10zu, %10zu) %10zu\n", target_start_, target_len_, data_.size()); + Write8(fd, static_cast(source_start_)); + Write8(fd, static_cast(source_len_)); + Write8(fd, static_cast(offset)); + return offset + data_.size(); + case CHUNK_DEFLATE: + printf("deflate (%10zu, %10zu) %10zu\n", target_start_, target_len_, data_.size()); + Write8(fd, static_cast(source_start_)); + Write8(fd, static_cast(source_len_)); + Write8(fd, static_cast(offset)); + Write8(fd, static_cast(source_uncompressed_len_)); + Write8(fd, static_cast(target_uncompressed_len_)); + Write4(fd, target_compress_level_); + Write4(fd, ImageChunk::METHOD); + Write4(fd, ImageChunk::WINDOWBITS); + Write4(fd, ImageChunk::MEMLEVEL); + Write4(fd, ImageChunk::STRATEGY); + return offset + data_.size(); + case CHUNK_RAW: + printf("raw (%10zu, %10zu)\n", target_start_, target_len_); + Write4(fd, static_cast(data_.size())); + if (!android::base::WriteFully(fd, data_.data(), data_.size())) { + CHECK(false) << "failed to write " << data_.size() << " bytes patch"; + } + return offset; + default: + CHECK(false) << "unexpected chunk type: " << type_; + return offset; + } +} + +// Write the contents of |patch_chunks| to |patch_fd|. +bool PatchChunk::WritePatchDataToFd(const std::vector& patch_chunks, int patch_fd) { + // Figure out how big the imgdiff file header is going to be, so that we can correctly compute + // the offset of each bsdiff patch within the file. + size_t total_header_size = 12; + for (const auto& patch : patch_chunks) { + total_header_size += patch.GetHeaderSize(); + } + + size_t offset = total_header_size; + + // Write out the headers. + if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) { + printf("failed to write \"IMGDIFF2\": %s\n", strerror(errno)); + return false; + } + + Write4(patch_fd, static_cast(patch_chunks.size())); + for (size_t i = 0; i < patch_chunks.size(); ++i) { + printf("chunk %zu: ", i); + offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset); + } + + // Append each chunk's bsdiff patch, in order. + for (const auto& patch : patch_chunks) { + if (patch.type_ == CHUNK_RAW) { + continue; + } + if (!android::base::WriteFully(patch_fd, patch.data_.data(), patch.data_.size())) { + printf("failed to write %zu bytes patch to patch_fd\n", patch.data_.size()); + return false; + } + } + + return true; +} + // Interface for zip_mode and image_mode images. We initialize the image from an input file and // split the file content into a list of image chunks. class Image { @@ -548,8 +593,7 @@ class Image { // also if |find_normal| is true. ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false); - // Write the contents of |patch_data| to |patch_fd|. - bool WritePatchDataToFd(const std::vector>& patch_data, int patch_fd) const; + const ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false) const; void DumpChunks() const; @@ -561,10 +605,15 @@ class Image { std::vector::iterator end() { return chunks_.end(); } - // Return a pointer to the ith ImageChunk. - ImageChunk* Get(size_t i) { + + ImageChunk& operator[](size_t i) { CHECK_LT(i, chunks_.size()); - return &chunks_[i]; + return chunks_[i]; + } + + const ImageChunk& operator[](size_t i) const { + CHECK_LT(i, chunks_.size()); + return chunks_[i]; } size_t NumOfChunks() const { @@ -601,7 +650,7 @@ void Image::MergeAdjacentNormalChunks() { } } -ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) { +const ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) const { if (name.empty()) { return nullptr; } @@ -613,40 +662,9 @@ ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) { return nullptr; } -bool Image::WritePatchDataToFd(const std::vector>& patch_data, - int patch_fd) const { - // Figure out how big the imgdiff file header is going to be, so that we can correctly compute - // the offset of each bsdiff patch within the file. - CHECK_EQ(chunks_.size(), patch_data.size()); - size_t total_header_size = 12; - for (size_t i = 0; i < chunks_.size(); ++i) { - total_header_size += chunks_[i].GetHeaderSize(patch_data[i].size()); - } - - size_t offset = total_header_size; - - // Write out the headers. - if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) { - printf("failed to write \"IMGDIFF2\": %s\n", strerror(errno)); - return false; - } - Write4(patch_fd, static_cast(chunks_.size())); - for (size_t i = 0; i < chunks_.size(); ++i) { - printf("chunk %zu: ", i); - offset = chunks_[i].WriteHeaderToFd(patch_fd, patch_data[i], offset); - } - - // Append each chunk's bsdiff patch, in order. - for (size_t i = 0; i < chunks_.size(); ++i) { - if (chunks_[i].GetType() != CHUNK_RAW) { - if (!android::base::WriteFully(patch_fd, patch_data[i].data(), patch_data[i].size())) { - printf("failed to write %zu bytes patch for chunk %zu\n", patch_data[i].size(), i); - return false; - } - } - } - - return true; +ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) { + return const_cast( + static_cast(this)->FindChunkByName(name, find_normal)); } void Image::DumpChunks() const { @@ -699,8 +717,8 @@ class ZipModeImage : public Image { // src and tgt are identical. static bool CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image); - // Compute the patches against the input image, and write the data into |patch_name|. - static bool GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_image, + // Compute the patch between tgt & src images, and write the data into |patch_name|. + static bool GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image, const std::string& patch_name); private: @@ -834,14 +852,11 @@ bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::strin ErrorCodeString(ret)); return false; } - ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len); - curr.SetEntryName(entry_name); + ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len, entry_name); curr.SetUncompressedData(std::move(uncompressed_data)); - chunks_.push_back(curr); + chunks_.push_back(std::move(curr)); } else { - ImageChunk curr(CHUNK_NORMAL, entry->offset, &file_content_, compressed_len); - curr.SetEntryName(entry_name); - chunks_.push_back(curr); + chunks_.emplace_back(CHUNK_NORMAL, entry->offset, &file_content_, compressed_len, entry_name); } return true; @@ -907,40 +922,55 @@ bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* } } - return true; -} - -bool ZipModeImage::GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_image, - const std::string& patch_name) { // For zips, we only need merge normal chunks for the target: deflated chunks are matched via // filename, and normal chunks are patched using the entire source file as the source. tgt_image->MergeAdjacentNormalChunks(); tgt_image->DumpChunks(); - printf("Construct patches for %zu chunks...\n", tgt_image->NumOfChunks()); - std::vector> patch_data(tgt_image->NumOfChunks()); + return true; +} + +bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image, + const std::string& patch_name) { + printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks()); + std::vector patch_chunks; + patch_chunks.reserve(tgt_image.NumOfChunks()); saidx_t* bsdiff_cache = nullptr; - size_t i = 0; - for (auto& tgt_chunk : *tgt_image) { - ImageChunk* src_chunk = (tgt_chunk.GetType() != CHUNK_DEFLATE) - ? nullptr - : src_image->FindChunkByName(tgt_chunk.GetEntryName()); + for (size_t i = 0; i < tgt_image.NumOfChunks(); i++) { + const auto& tgt_chunk = tgt_image[i]; + + if (PatchChunk::RawDataIsSmaller(tgt_chunk, 0)) { + patch_chunks.emplace_back(tgt_chunk); + continue; + } - const auto& src_ref = (src_chunk == nullptr) ? src_image->PseudoSource() : *src_chunk; + const ImageChunk* src_chunk = (tgt_chunk.GetType() != CHUNK_DEFLATE) + ? nullptr + : src_image.FindChunkByName(tgt_chunk.GetEntryName()); + + const auto& src_ref = (src_chunk == nullptr) ? src_image.PseudoSource() : *src_chunk; saidx_t** bsdiff_cache_ptr = (src_chunk == nullptr) ? &bsdiff_cache : nullptr; - if (!tgt_chunk.MakePatch(src_ref, &patch_data[i], bsdiff_cache_ptr)) { + std::vector patch_data; + if (!ImageChunk::MakePatch(tgt_chunk, src_ref, &patch_data, bsdiff_cache_ptr)) { printf("Failed to generate patch, name: %s\n", tgt_chunk.GetEntryName().c_str()); return false; } - printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(), + printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(), tgt_chunk.GetRawDataLength()); - i++; + + if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) { + patch_chunks.emplace_back(tgt_chunk); + } else { + patch_chunks.emplace_back(tgt_chunk, src_ref, std::move(patch_data)); + } } free(bsdiff_cache); + CHECK_EQ(tgt_image.NumOfChunks(), patch_chunks.size()); + android::base::unique_fd patch_fd( open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); if (patch_fd == -1) { @@ -948,7 +978,7 @@ bool ZipModeImage::GeneratePatches(ZipModeImage* tgt_image, ZipModeImage* src_im return false; } - return tgt_image->WritePatchDataToFd(patch_data, patch_fd); + return PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd); } class ImageModeImage : public Image { @@ -958,14 +988,16 @@ class ImageModeImage : public Image { // Initialize the image chunks list by searching the magic numbers in an image file. bool Initialize(const std::string& filename) override; + bool SetBonusData(const std::vector& bonus_data); + // In Image Mode, verify that the source and target images have the same chunk structure (ie, the // same sequence of deflate and normal chunks). static bool CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image); // In image mode, generate patches against the given source chunks and bonus_data; write the // result to |patch_name|. - static bool GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* src_image, - const std::vector& bonus_data, const std::string& patch_name); + static bool GeneratePatches(const ImageModeImage& tgt_image, const ImageModeImage& src_image, + const std::string& patch_name); }; bool ImageModeImage::Initialize(const std::string& filename) { @@ -1053,7 +1085,7 @@ bool ImageModeImage::Initialize(const std::string& filename) { ImageChunk body(CHUNK_DEFLATE, pos, &file_content_, raw_data_len); uncompressed_data.resize(uncompressed_len); body.SetUncompressedData(std::move(uncompressed_data)); - chunks_.push_back(body); + chunks_.push_back(std::move(body)); pos += raw_data_len; @@ -1083,6 +1115,18 @@ bool ImageModeImage::Initialize(const std::string& filename) { return true; } +bool ImageModeImage::SetBonusData(const std::vector& bonus_data) { + CHECK(is_source_); + if (chunks_.size() < 2 || !chunks_[1].SetBonusData(bonus_data)) { + printf("Failed to set bonus data\n"); + DumpChunks(); + return false; + } + + printf(" using %zu bytes of bonus data\n", bonus_data.size()); + return true; +} + // In Image Mode, verify that the source and target images have the same chunk structure (ie, the // same sequence of deflate and normal chunks). bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image) { @@ -1097,7 +1141,7 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI return false; } for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) { - if (tgt_image->Get(i)->GetType() != src_image->Get(i)->GetType()) { + if ((*tgt_image)[i].GetType() != (*src_image)[i].GetType()) { printf("source and target don't have same chunk structure! (chunk %zu)\n", i); tgt_image->DumpChunks(); src_image->DumpChunks(); @@ -1106,26 +1150,23 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI } for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) { - auto& tgt_chunk = *tgt_image->Get(i); - auto& src_chunk = *src_image->Get(i); + auto& tgt_chunk = (*tgt_image)[i]; + auto& src_chunk = (*src_image)[i]; if (tgt_chunk.GetType() != CHUNK_DEFLATE) { continue; } - // Confirm that we can recompress the data and get exactly the same bits as are in the - // input target image. - if (!tgt_chunk.ReconstructDeflateChunk()) { - printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i, - tgt_chunk.GetEntryName().c_str()); - tgt_chunk.ChangeDeflateChunkToNormal(); - src_chunk.ChangeDeflateChunkToNormal(); - continue; - } - // If two deflate chunks are identical treat them as normal chunks. if (tgt_chunk == src_chunk) { tgt_chunk.ChangeDeflateChunkToNormal(); src_chunk.ChangeDeflateChunkToNormal(); + } else if (!tgt_chunk.ReconstructDeflateChunk()) { + // We cannot recompress the data and get exactly the same bits as are in the input target + // image, fall back to normal + printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i, + tgt_chunk.GetEntryName().c_str()); + tgt_chunk.ChangeDeflateChunkToNormal(); + src_chunk.ChangeDeflateChunkToNormal(); } } @@ -1144,29 +1185,39 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI // In image mode, generate patches against the given source chunks and bonus_data; write the // result to |patch_name|. -bool ImageModeImage::GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* src_image, - const std::vector& bonus_data, +bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image, + const ImageModeImage& src_image, const std::string& patch_name) { - printf("Construct patches for %zu chunks...\n", tgt_image->NumOfChunks()); - std::vector> patch_data(tgt_image->NumOfChunks()); + printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks()); + std::vector patch_chunks; + patch_chunks.reserve(tgt_image.NumOfChunks()); - for (size_t i = 0; i < tgt_image->NumOfChunks(); i++) { - auto& tgt_chunk = *tgt_image->Get(i); - auto& src_chunk = *src_image->Get(i); + for (size_t i = 0; i < tgt_image.NumOfChunks(); i++) { + const auto& tgt_chunk = tgt_image[i]; + const auto& src_chunk = src_image[i]; - if (i == 1 && !bonus_data.empty()) { - printf(" using %zu bytes of bonus data for chunk %zu\n", bonus_data.size(), i); - src_chunk.SetBonusData(bonus_data); + if (PatchChunk::RawDataIsSmaller(tgt_chunk, 0)) { + patch_chunks.emplace_back(tgt_chunk); + continue; } - if (!tgt_chunk.MakePatch(src_chunk, &patch_data[i], nullptr)) { + std::vector patch_data; + if (!ImageChunk::MakePatch(tgt_chunk, src_chunk, &patch_data, nullptr)) { printf("Failed to generate patch for target chunk %zu: ", i); return false; } - printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data[i].size(), + printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(), tgt_chunk.GetRawDataLength()); + + if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) { + patch_chunks.emplace_back(tgt_chunk); + } else { + patch_chunks.emplace_back(tgt_chunk, src_chunk, std::move(patch_data)); + } } + CHECK_EQ(tgt_image.NumOfChunks(), patch_chunks.size()); + android::base::unique_fd patch_fd( open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); if (patch_fd == -1) { @@ -1174,7 +1225,7 @@ bool ImageModeImage::GeneratePatches(ImageModeImage* tgt_image, ImageModeImage* return false; } - return tgt_image->WritePatchDataToFd(patch_data, patch_fd); + return PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd); } int imgdiff(int argc, const char** argv) { @@ -1236,7 +1287,7 @@ int imgdiff(int argc, const char** argv) { } // Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of // deflate chunks). - if (!ZipModeImage::GeneratePatches(&tgt_image, &src_image, argv[optind + 2])) { + if (!ZipModeImage::GeneratePatches(tgt_image, src_image, argv[optind + 2])) { return 1; } } else { @@ -1253,7 +1304,12 @@ int imgdiff(int argc, const char** argv) { if (!ImageModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) { return 1; } - if (!ImageModeImage::GeneratePatches(&tgt_image, &src_image, bonus_data, argv[optind + 2])) { + + if (!bonus_data.empty() && !src_image.SetBonusData(bonus_data)) { + return 1; + } + + if (!ImageModeImage::GeneratePatches(tgt_image, src_image, argv[optind + 2])) { return 1; } } -- cgit v1.2.3 From a009ce05e28f83d1bf7e5bb48f7a094d888b5aa6 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 15 Aug 2017 11:45:49 -0700 Subject: update_verifier now logs to kmesg Set up update_verifier logging to be written to kmsg; because we may not have Logd during boot time. Bug: 64713327 Test: logs show up in `adb shell dmesg` Change-Id: If02f460bda121cd3e9062bc0e08107c6da66492c --- update_verifier/update_verifier_main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/update_verifier/update_verifier_main.cpp b/update_verifier/update_verifier_main.cpp index 46e8bbb59..9dd5a0cc4 100644 --- a/update_verifier/update_verifier_main.cpp +++ b/update_verifier/update_verifier_main.cpp @@ -16,8 +16,14 @@ // See the comments in update_verifier.cpp. +#include + #include "update_verifier/update_verifier.h" int main(int argc, char** argv) { + // Set up update_verifier logging to be written to kmsg; because we may not have Logd during + // boot time. + android::base::InitLogging(argv, &android::base::KernelLogger); + return update_verifier(argc, argv); } -- cgit v1.2.3 From 2bbc6d642d1fbfb007905d95b629fe5f833b2a1b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 13 Aug 2017 23:48:55 -0700 Subject: screen_ui: Word-wrap menu headers. This CL adds ScreenRecoveryUI::DrawWrappedTextLines() to better handle long menu header texts. It does a word wrap at spaces, if available. This avoids fixed-length menu headers being truncated on small screens. Bug: 64293520 Test: On bullhead, boot into recovery with --prompt_and_wipe_data, and check the prompt texts. Change-Id: Ia22746583516dd230567a267584aca558429395e --- recovery.cpp | 9 +++++---- screen_ui.cpp | 30 +++++++++++++++++++++++++++++- screen_ui.h | 3 +++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 233e56246..c5ead451a 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -757,12 +757,13 @@ static bool wipe_data(Device* device) { } static bool prompt_and_wipe_data(Device* device) { + // Use a single string and let ScreenRecoveryUI handles the wrapping. const char* const headers[] = { - "Can't load Android system. Your data may be corrupt.", - "If you continue to get this message, you may need to", - "perform a factory data reset and erase all user data", + "Can't load Android system. Your data may be corrupt. " + "If you continue to get this message, you may need to " + "perform a factory data reset and erase all user data " "stored on this device.", - NULL + nullptr }; const char* const items[] = { "Try again", diff --git a/screen_ui.cpp b/screen_ui.cpp index 8f792f162..e056512bd 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -278,6 +278,34 @@ int ScreenRecoveryUI::DrawTextLines(int x, int y, const char* const* lines) cons return offset; } +int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y, const char* const* lines) const { + int offset = 0; + for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { + // The line will be wrapped if it exceeds text_cols_. + std::string line(lines[i]); + size_t next_start = 0; + while (next_start < line.size()) { + std::string sub = line.substr(next_start, text_cols_ + 1); + if (sub.size() <= text_cols_) { + next_start += sub.size(); + } else { + // Line too long and must be wrapped to text_cols_ columns. + size_t last_space = sub.find_last_of(" \t\n"); + if (last_space == std::string::npos) { + // No space found, just draw as much as we can + sub.resize(text_cols_); + next_start += text_cols_; + } else { + sub.resize(last_space); + next_start += last_space + 1; + } + } + offset += DrawTextLine(x, y + offset, sub.c_str(), false); + } + } + return offset; +} + static const char* REGULAR_HELP[] = { "Use volume up/down and power.", NULL @@ -316,7 +344,7 @@ void ScreenRecoveryUI::draw_screen_locked() { y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); SetColor(HEADER); - y += DrawTextLines(x, y, menu_headers_); + y += DrawWrappedTextLines(x, y, menu_headers_); SetColor(MENU); y += DrawHorizontalRule(y) + 4; diff --git a/screen_ui.h b/screen_ui.h index 8402fac00..df7cc25b3 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -187,6 +187,9 @@ class ScreenRecoveryUI : public RecoveryUI { virtual int DrawTextLine(int x, int y, const char* line, bool bold) const; // Draws multiple text lines. Returns the offset it should be moving along Y-axis. int DrawTextLines(int x, int y, const char* const* lines) const; + // Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines. + // Returns the offset it should be moving along Y-axis. + int DrawWrappedTextLines(int x, int y, const char* const* lines) const; }; #endif // RECOVERY_UI_H -- cgit v1.2.3 From 13aa4a902ba2fa304fc9fe826f797a4e0e2182b7 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 16 Aug 2017 13:25:55 -0700 Subject: screen_ui: Fix a case that may truncate the last char. ScreenRecoveryUI::DrawWrappedTextLines() should be called with kMarginWidth only. Because it's using a line limit of text_cols_, which is unaware of kMenuIdent. Bug: 64293520 Test: No missing char with long header text. Change-Id: Ib4d08de2c56473a483ff9964eb6cec31f8a74c9a --- screen_ui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index e056512bd..a02550199 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -344,7 +344,8 @@ void ScreenRecoveryUI::draw_screen_locked() { y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); SetColor(HEADER); - y += DrawWrappedTextLines(x, y, menu_headers_); + // Ignore kMenuIndent, which is not taken into account by text_cols_. + y += DrawWrappedTextLines(kMarginWidth, y, menu_headers_); SetColor(MENU); y += DrawHorizontalRule(y) + 4; -- cgit v1.2.3 From e332c5ad0e83c36683f8d1a1d5dd795d17b4aada Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 17 Aug 2017 00:34:01 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import Bug: 64680434 Change-Id: Ie356071f322238a5fb3a4610d2a79141c9aa9377 --- tools/recovery_l10n/res/values-az/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-b+sr+Latn/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-be/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-bs/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-en-rAU/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-en-rCA/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-en-rXC/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-gu/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-kk/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-kn/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-pa/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-pt-rBR/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-sq/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-te/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-ur/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-uz/strings.xml | 9 +++++++++ 16 files changed, 144 insertions(+) create mode 100644 tools/recovery_l10n/res/values-az/strings.xml create mode 100644 tools/recovery_l10n/res/values-b+sr+Latn/strings.xml create mode 100644 tools/recovery_l10n/res/values-be/strings.xml create mode 100644 tools/recovery_l10n/res/values-bs/strings.xml create mode 100644 tools/recovery_l10n/res/values-en-rAU/strings.xml create mode 100644 tools/recovery_l10n/res/values-en-rCA/strings.xml create mode 100644 tools/recovery_l10n/res/values-en-rXC/strings.xml create mode 100644 tools/recovery_l10n/res/values-gu/strings.xml create mode 100644 tools/recovery_l10n/res/values-kk/strings.xml create mode 100644 tools/recovery_l10n/res/values-kn/strings.xml create mode 100644 tools/recovery_l10n/res/values-pa/strings.xml create mode 100644 tools/recovery_l10n/res/values-pt-rBR/strings.xml create mode 100644 tools/recovery_l10n/res/values-sq/strings.xml create mode 100644 tools/recovery_l10n/res/values-te/strings.xml create mode 100644 tools/recovery_l10n/res/values-ur/strings.xml create mode 100644 tools/recovery_l10n/res/values-uz/strings.xml diff --git a/tools/recovery_l10n/res/values-az/strings.xml b/tools/recovery_l10n/res/values-az/strings.xml new file mode 100644 index 000000000..c6765a9ea --- /dev/null +++ b/tools/recovery_l10n/res/values-az/strings.xml @@ -0,0 +1,9 @@ + + + "Sistem güncəlləməsi quraşdırılır..." + "Silinir" + "Əmr yoxdur" + "Xəta!" + "Təhlükəsizlik güncəlləməsi yüklənir" + diff --git a/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml new file mode 100644 index 000000000..c2d8f2239 --- /dev/null +++ b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,9 @@ + + + "Ažuriranje sistema se instalira" + "Briše se" + "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 new file mode 100644 index 000000000..7c0954d31 --- /dev/null +++ b/tools/recovery_l10n/res/values-be/strings.xml @@ -0,0 +1,9 @@ + + + "Усталёўка абнаўлення сістэмы" + "Сціранне" + "Няма каманды" + "Памылка" + "Усталёўка абнаўлення сістэмы бяспекі" + diff --git a/tools/recovery_l10n/res/values-bs/strings.xml b/tools/recovery_l10n/res/values-bs/strings.xml new file mode 100644 index 000000000..412cf0276 --- /dev/null +++ b/tools/recovery_l10n/res/values-bs/strings.xml @@ -0,0 +1,9 @@ + + + "Ažuriranje sistema…" + "Brisanje u toku" + "Nema komande" + "Greška!" + "Instaliranje sigurnosnog ažuriranja…" + diff --git a/tools/recovery_l10n/res/values-en-rAU/strings.xml b/tools/recovery_l10n/res/values-en-rAU/strings.xml new file mode 100644 index 000000000..dc75c2374 --- /dev/null +++ b/tools/recovery_l10n/res/values-en-rAU/strings.xml @@ -0,0 +1,9 @@ + + + "Installing system update" + "Erasing" + "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 new file mode 100644 index 000000000..dc75c2374 --- /dev/null +++ b/tools/recovery_l10n/res/values-en-rCA/strings.xml @@ -0,0 +1,9 @@ + + + "Installing system update" + "Erasing" + "No command" + "Error!" + "Installing security update" + diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml new file mode 100644 index 000000000..2d528b3fb --- /dev/null +++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml @@ -0,0 +1,9 @@ + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Installing system update‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎Erasing‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎" + diff --git a/tools/recovery_l10n/res/values-gu/strings.xml b/tools/recovery_l10n/res/values-gu/strings.xml new file mode 100644 index 000000000..2355a0f4f --- /dev/null +++ b/tools/recovery_l10n/res/values-gu/strings.xml @@ -0,0 +1,9 @@ + + + "સિસ્ટમ અપડેટ ઇન્સ્ટૉલ કરી રહ્યાં છે" + "કાઢી નાખી રહ્યું છે" + "કોઈ આદેશ નથી" + "ભૂલ!" + "સુરક્ષા અપડેટ ઇન્સ્ટૉલ કરી રહ્યાં છે" + diff --git a/tools/recovery_l10n/res/values-kk/strings.xml b/tools/recovery_l10n/res/values-kk/strings.xml new file mode 100644 index 000000000..a4bd86e66 --- /dev/null +++ b/tools/recovery_l10n/res/values-kk/strings.xml @@ -0,0 +1,9 @@ + + + "Жүйе жаңартуы орнатылуда" + "Өшірілуде" + "Пәрмен жоқ" + "Қате!" + "Қауіпсіздік жаңартуы орнатылуда" + diff --git a/tools/recovery_l10n/res/values-kn/strings.xml b/tools/recovery_l10n/res/values-kn/strings.xml new file mode 100644 index 000000000..5bf6260ee --- /dev/null +++ b/tools/recovery_l10n/res/values-kn/strings.xml @@ -0,0 +1,9 @@ + + + "ಸಿಸ್ಟಂ ಅಪ್‌ಡೇಟ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" + "ಅಳಿಸಲಾಗುತ್ತಿದೆ" + "ಯಾವುದೇ ಆದೇಶವಿಲ್ಲ" + "ದೋಷ!" + "ಭದ್ರತೆಯ ಅಪ್‌ಡೇಟ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" + diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml new file mode 100644 index 000000000..8564c9c36 --- /dev/null +++ b/tools/recovery_l10n/res/values-pa/strings.xml @@ -0,0 +1,9 @@ + + + "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + "ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ" + "ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ" + "ਅਸ਼ੁੱਧੀ!" + "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + diff --git a/tools/recovery_l10n/res/values-pt-rBR/strings.xml b/tools/recovery_l10n/res/values-pt-rBR/strings.xml new file mode 100644 index 000000000..b72704385 --- /dev/null +++ b/tools/recovery_l10n/res/values-pt-rBR/strings.xml @@ -0,0 +1,9 @@ + + + "Instalando atualização do sistema" + "Apagando" + "Nenhum comando" + "Erro!" + "Instalando atualização de segurança" + diff --git a/tools/recovery_l10n/res/values-sq/strings.xml b/tools/recovery_l10n/res/values-sq/strings.xml new file mode 100644 index 000000000..1156931fb --- /dev/null +++ b/tools/recovery_l10n/res/values-sq/strings.xml @@ -0,0 +1,9 @@ + + + "Po instalon përditësimin e sistemit" + "Po spastron" + "Nuk ka komanda" + "Gabim!" + "Po instalon përditësimin e sigurisë" + diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml new file mode 100644 index 000000000..cfb02c915 --- /dev/null +++ b/tools/recovery_l10n/res/values-te/strings.xml @@ -0,0 +1,9 @@ + + + "సిస్టమ్ నవీకరణను ఇన్‍స్టాల్ చేస్తోంది" + "డేటాను తొలగిస్తోంది" + "ఆదేశం లేదు" + "లోపం సంభవించింది!" + "భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది" + diff --git a/tools/recovery_l10n/res/values-ur/strings.xml b/tools/recovery_l10n/res/values-ur/strings.xml new file mode 100644 index 000000000..12e32fbc1 --- /dev/null +++ b/tools/recovery_l10n/res/values-ur/strings.xml @@ -0,0 +1,9 @@ + + + "سسٹم اپ ڈیٹ انسٹال ہو رہی ہے" + "صاف ہو رہا ہے" + "کوئی کمانڈ نہیں ہے" + "خرابی!" + "سیکیورٹی اپ ڈیٹ انسٹال ہو رہی ہے" + diff --git a/tools/recovery_l10n/res/values-uz/strings.xml b/tools/recovery_l10n/res/values-uz/strings.xml new file mode 100644 index 000000000..2c309d646 --- /dev/null +++ b/tools/recovery_l10n/res/values-uz/strings.xml @@ -0,0 +1,9 @@ + + + "Tizim yangilanishi o‘rnatilmoqda" + "Tozalanmoqda…" + "Buyruq yo‘q" + "Xato!" + "Xavfsizlik yangilanishi o‘rnatilmoqda" + -- cgit v1.2.3 From 57dd96199570beb29ea8b0f3934c594cd42e3043 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 17 Aug 2017 17:50:56 -0700 Subject: Move Image/ImageChunk/PatchChunk declaration into header files 1. Move the declaration of the Image classes to the header file to make testing easier. 2. Also move rangeset.h to bootable/recovery to allow access in imgdiff. Test: recovery component test Change-Id: I68a863e60a3f2e7ae46ee48f48eb15391f5f4330 --- applypatch/Android.mk | 9 +- applypatch/imgdiff.cpp | 335 +++++--------------------- applypatch/include/applypatch/imgdiff_image.h | 247 +++++++++++++++++++ rangeset.h | 278 +++++++++++++++++++++ tests/unit/rangeset_test.cpp | 2 +- updater/blockimg.cpp | 2 +- updater/include/updater/rangeset.h | 278 --------------------- 7 files changed, 599 insertions(+), 552 deletions(-) create mode 100644 applypatch/include/applypatch/imgdiff_image.h create mode 100644 rangeset.h delete mode 100644 updater/include/updater/rangeset.h diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 7aed0a95a..e38207c22 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -151,7 +151,8 @@ LOCAL_CFLAGS := \ LOCAL_STATIC_LIBRARIES := \ $(libimgdiff_static_libraries) LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include + $(LOCAL_PATH)/include \ + bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_STATIC_LIBRARY) @@ -166,7 +167,8 @@ LOCAL_CFLAGS := \ LOCAL_STATIC_LIBRARIES := \ $(libimgdiff_static_libraries) LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include + $(LOCAL_PATH)/include \ + bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_HOST_STATIC_LIBRARY) @@ -180,4 +182,7 @@ LOCAL_STATIC_LIBRARIES := \ libimgdiff \ $(libimgdiff_static_libraries) \ libbz +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include \ + bootable/recovery include $(BUILD_HOST_EXECUTABLE) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index a81e385a3..59b600713 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -140,11 +140,12 @@ #include #include #include -#include - #include +#include #include +#include "applypatch/imgdiff_image.h" + using android::base::get_unaligned; static constexpr auto BUFFER_SIZE = 0x8000; @@ -161,99 +162,16 @@ static inline bool Write4(int fd, int32_t value) { return android::base::WriteFully(fd, &value, sizeof(int32_t)); } -class ImageChunk { - public: - static constexpr auto WINDOWBITS = -15; // 32kb window; negative to indicate a raw stream. - static constexpr auto MEMLEVEL = 8; // the default value. - static constexpr auto METHOD = Z_DEFLATED; - static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY; - - ImageChunk(int type, size_t start, const std::vector* file_content, size_t raw_data_len, - std::string entry_name = {}) - : type_(type), - start_(start), - input_file_ptr_(file_content), - raw_data_len_(raw_data_len), - compress_level_(6), - entry_name_(std::move(entry_name)) { - CHECK(file_content != nullptr) << "input file container can't be nullptr"; - } - - int GetType() const { - return type_; - } - size_t GetRawDataLength() const { - return raw_data_len_; - } - const std::string& GetEntryName() const { - return entry_name_; - } - size_t GetStartOffset() const { - return start_; - } - int GetCompressLevel() const { - return compress_level_; - } - - // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return - // the raw data. - const uint8_t * DataForPatch() const; - size_t DataLengthForPatch() const; - - void Dump() const { - printf("type: %d, start: %zu, len: %zu, name: %s\n", type_, start_, DataLengthForPatch(), - entry_name_.c_str()); - } - - void SetUncompressedData(std::vector data); - bool SetBonusData(const std::vector& bonus_data); - - bool operator==(const ImageChunk& other) const; - bool operator!=(const ImageChunk& other) const { - return !(*this == other); - } - - /* - * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob of uninterpreted data). - * The resulting patch will likely be about as big as the target file, but it lets us handle - * the case of images where some gzip chunks are reconstructible but others aren't (by treating - * the ones that aren't as normal chunks). - */ - void ChangeDeflateChunkToNormal(); - - /* - * Verify that we can reproduce exactly the same compressed data that we started with. Sets the - * level, method, windowBits, memLevel, and strategy fields in the chunk to the encoding - * parameters needed to produce the right output. - */ - bool ReconstructDeflateChunk(); - bool IsAdjacentNormal(const ImageChunk& other) const; - void MergeAdjacentNormal(const ImageChunk& other); - - /* - * Compute a bsdiff patch between |src| and |tgt|; Store the result in the patch_data. - * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk is used - * repeatedly, pass nullptr if not needed. - */ - static bool MakePatch(const ImageChunk& tgt, const ImageChunk& src, - std::vector* patch_data, saidx_t** bsdiff_cache); - - private: - const uint8_t* GetRawData() const; - bool TryReconstruction(int level); - - int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW - size_t start_; // offset of chunk in the original input file - const std::vector* input_file_ptr_; // ptr to the full content of original input file - size_t raw_data_len_; - - // deflate encoder parameters - int compress_level_; - - // --- for CHUNK_DEFLATE chunks only: --- - std::vector uncompressed_data_; - std::string entry_name_; // used for zip entries -}; +ImageChunk::ImageChunk(int type, size_t start, const std::vector* file_content, + size_t raw_data_len, std::string entry_name) + : type_(type), + start_(start), + input_file_ptr_(file_content), + raw_data_len_(raw_data_len), + compress_level_(6), + entry_name_(std::move(entry_name)) { + CHECK(file_content != nullptr) << "input file container can't be nullptr"; +} const uint8_t* ImageChunk::GetRawData() const { CHECK_LE(start_ + raw_data_len_, input_file_ptr_->size()); @@ -424,57 +342,28 @@ bool ImageChunk::TryReconstruction(int level) { return true; } -// PatchChunk stores the patch data between a source chunk and a target chunk. It also keeps track -// of the metadata of src&tgt chunks (e.g. offset, raw data length, uncompressed data length). -class PatchChunk { - public: - PatchChunk(const ImageChunk& tgt, const ImageChunk& src, std::vector data) - : type_(tgt.GetType()), - source_start_(src.GetStartOffset()), - source_len_(src.GetRawDataLength()), - source_uncompressed_len_(src.DataLengthForPatch()), - target_start_(tgt.GetStartOffset()), - target_len_(tgt.GetRawDataLength()), - target_uncompressed_len_(tgt.DataLengthForPatch()), - target_compress_level_(tgt.GetCompressLevel()), - data_(std::move(data)) {} - - // Construct a CHUNK_RAW patch from the target data directly. - explicit PatchChunk(const ImageChunk& tgt) - : type_(CHUNK_RAW), - source_start_(0), - source_len_(0), - source_uncompressed_len_(0), - target_start_(tgt.GetStartOffset()), - target_len_(tgt.GetRawDataLength()), - target_uncompressed_len_(tgt.DataLengthForPatch()), - target_compress_level_(tgt.GetCompressLevel()), - data_(tgt.DataForPatch(), tgt.DataForPatch() + tgt.DataLengthForPatch()) {} - - // Return true if raw data size is smaller than the patch size. - static bool RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size); - - static bool WritePatchDataToFd(const std::vector& patch_chunks, int patch_fd); - - private: - size_t GetHeaderSize() const; - size_t WriteHeaderToFd(int fd, size_t offset) const; - - // The patch chunk type is the same as the target chunk type. The only exception is we change - // the |type_| to CHUNK_RAW if target length is smaller than the patch size. - int type_; - - size_t source_start_; - size_t source_len_; - size_t source_uncompressed_len_; - - size_t target_start_; // offset of the target chunk within the target file - size_t target_len_; - size_t target_uncompressed_len_; - size_t target_compress_level_; // the deflate compression level of the target chunk. - - std::vector data_; // storage for the patch data -}; +PatchChunk::PatchChunk(const ImageChunk& tgt, const ImageChunk& src, std::vector data) + : type_(tgt.GetType()), + source_start_(src.GetStartOffset()), + source_len_(src.GetRawDataLength()), + source_uncompressed_len_(src.DataLengthForPatch()), + target_start_(tgt.GetStartOffset()), + target_len_(tgt.GetRawDataLength()), + target_uncompressed_len_(tgt.DataLengthForPatch()), + target_compress_level_(tgt.GetCompressLevel()), + data_(std::move(data)) {} + +// Construct a CHUNK_RAW patch from the target data directly. +PatchChunk::PatchChunk(const ImageChunk& tgt) + : type_(CHUNK_RAW), + source_start_(0), + source_len_(0), + source_uncompressed_len_(0), + target_start_(tgt.GetStartOffset()), + target_len_(tgt.GetRawDataLength()), + target_uncompressed_len_(tgt.DataLengthForPatch()), + target_compress_level_(tgt.GetCompressLevel()), + data_(tgt.DataForPatch(), tgt.DataForPatch() + tgt.DataLengthForPatch()) {} // Return true if raw data is smaller than the patch size. bool PatchChunk::RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size) { @@ -574,59 +463,15 @@ bool PatchChunk::WritePatchDataToFd(const std::vector& patch_chunks, return true; } -// Interface for zip_mode and image_mode images. We initialize the image from an input file and -// split the file content into a list of image chunks. -class Image { - public: - explicit Image(bool is_source) : is_source_(is_source) {} - - virtual ~Image() {} - - // Create a list of image chunks from input file. - virtual bool Initialize(const std::string& filename) = 0; - - // Look for runs of adjacent normal chunks and compress them down into a single chunk. (Such - // runs can be produced when deflate chunks are changed to normal chunks.) - void MergeAdjacentNormalChunks(); - - // In zip mode, find the matching deflate source chunk by entry name. Search for normal chunks - // also if |find_normal| is true. - ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false); - - const ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false) const; - - void DumpChunks() const; - - // Non const iterators to access the stored ImageChunks. - std::vector::iterator begin() { - return chunks_.begin(); - } - - std::vector::iterator end() { - return chunks_.end(); - } - - ImageChunk& operator[](size_t i) { - CHECK_LT(i, chunks_.size()); - return chunks_[i]; - } - - const ImageChunk& operator[](size_t i) const { - CHECK_LT(i, chunks_.size()); - return chunks_[i]; - } - - size_t NumOfChunks() const { - return chunks_.size(); - } - - protected: - bool ReadFile(const std::string& filename, std::vector* file_content); +ImageChunk& Image::operator[](size_t i) { + CHECK_LT(i, chunks_.size()); + return chunks_[i]; +} - bool is_source_; // True if it's for source chunks. - std::vector chunks_; // Internal storage of ImageChunk. - std::vector file_content_; // Store the whole input file in memory. -}; +const ImageChunk& Image::operator[](size_t i) const { + CHECK_LT(i, chunks_.size()); + return chunks_[i]; +} void Image::MergeAdjacentNormalChunks() { size_t merged_last = 0, cur = 0; @@ -650,23 +495,6 @@ void Image::MergeAdjacentNormalChunks() { } } -const ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) const { - if (name.empty()) { - return nullptr; - } - for (auto& chunk : chunks_) { - if ((chunk.GetType() == CHUNK_DEFLATE || find_normal) && chunk.GetEntryName() == name) { - return &chunk; - } - } - return nullptr; -} - -ImageChunk* Image::FindChunkByName(const std::string& name, bool find_normal) { - return const_cast( - static_cast(this)->FindChunkByName(name, find_normal)); -} - void Image::DumpChunks() const { std::string type = is_source_ ? "source" : "target"; printf("Dumping chunks for %s\n", type.c_str()); @@ -701,39 +529,6 @@ bool Image::ReadFile(const std::string& filename, std::vector* file_con return true; } -class ZipModeImage : public Image { - public: - explicit ZipModeImage(bool is_source) : Image(is_source) {} - - bool Initialize(const std::string& filename) override; - - const ImageChunk& PseudoSource() const { - CHECK(is_source_); - CHECK(pseudo_source_ != nullptr); - return *pseudo_source_; - } - - // Verify that we can reconstruct the deflate chunks; also change the type to CHUNK_NORMAL if - // src and tgt are identical. - static bool CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image); - - // Compute the patch between tgt & src images, and write the data into |patch_name|. - static bool GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image, - const std::string& patch_name); - - private: - // Initialize image chunks based on the zip entries. - bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle); - // Add the a zip entry to the list. - bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry); - // Return the real size of the zip file. (omit the trailing zeros that used for alignment) - bool GetZipFileSize(size_t* input_file_size); - - // The pesudo source chunk for bsdiff if there's no match for the given target chunk. It's in - // fact the whole source file. - std::unique_ptr pseudo_source_; -}; - bool ZipModeImage::Initialize(const std::string& filename) { if (!ReadFile(filename, &file_content_)) { return false; @@ -754,9 +549,6 @@ bool ZipModeImage::Initialize(const std::string& filename) { return false; } - if (is_source_) { - pseudo_source_ = std::make_unique(CHUNK_NORMAL, 0, &file_content_, zipfile_size); - } if (!InitializeChunks(filename, handle)) { CloseArchive(handle); return false; @@ -895,6 +687,28 @@ bool ZipModeImage::GetZipFileSize(size_t* input_file_size) { return false; } +ImageChunk ZipModeImage::PseudoSource() const { + CHECK(is_source_); + return ImageChunk(CHUNK_NORMAL, 0, &file_content_, file_content_.size()); +} + +const ImageChunk* ZipModeImage::FindChunkByName(const std::string& name, bool find_normal) const { + if (name.empty()) { + return nullptr; + } + for (auto& chunk : chunks_) { + if ((chunk.GetType() == CHUNK_DEFLATE || find_normal) && chunk.GetEntryName() == name) { + return &chunk; + } + } + return nullptr; +} + +ImageChunk* ZipModeImage::FindChunkByName(const std::string& name, bool find_normal) { + return const_cast( + static_cast(this)->FindChunkByName(name, find_normal)); +} + bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image) { for (auto& tgt_chunk : *tgt_image) { if (tgt_chunk.GetType() != CHUNK_DEFLATE) { @@ -981,25 +795,6 @@ bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeI return PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd); } -class ImageModeImage : public Image { - public: - explicit ImageModeImage(bool is_source) : Image(is_source) {} - - // Initialize the image chunks list by searching the magic numbers in an image file. - bool Initialize(const std::string& filename) override; - - bool SetBonusData(const std::vector& bonus_data); - - // In Image Mode, verify that the source and target images have the same chunk structure (ie, the - // same sequence of deflate and normal chunks). - static bool CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image); - - // In image mode, generate patches against the given source chunks and bonus_data; write the - // result to |patch_name|. - static bool GeneratePatches(const ImageModeImage& tgt_image, const ImageModeImage& src_image, - const std::string& patch_name); -}; - bool ImageModeImage::Initialize(const std::string& filename) { if (!ReadFile(filename, &file_content_)) { return false; diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h new file mode 100644 index 000000000..221dd5ab5 --- /dev/null +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _APPLYPATCH_IMGDIFF_IMAGE_H +#define _APPLYPATCH_IMGDIFF_IMAGE_H + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "imgdiff.h" +#include "rangeset.h" + +class ImageChunk { + public: + static constexpr auto WINDOWBITS = -15; // 32kb window; negative to indicate a raw stream. + static constexpr auto MEMLEVEL = 8; // the default value. + static constexpr auto METHOD = Z_DEFLATED; + static constexpr auto STRATEGY = Z_DEFAULT_STRATEGY; + + ImageChunk(int type, size_t start, const std::vector* file_content, size_t raw_data_len, + std::string entry_name = {}); + + int GetType() const { + return type_; + } + size_t GetRawDataLength() const { + return raw_data_len_; + } + const std::string& GetEntryName() const { + return entry_name_; + } + size_t GetStartOffset() const { + return start_; + } + int GetCompressLevel() const { + return compress_level_; + } + + // CHUNK_DEFLATE will return the uncompressed data for diff, while other types will simply return + // the raw data. + const uint8_t* DataForPatch() const; + size_t DataLengthForPatch() const; + + void Dump() const { + printf("type: %d, start: %zu, len: %zu, name: %s\n", type_, start_, DataLengthForPatch(), + entry_name_.c_str()); + } + + void SetUncompressedData(std::vector data); + bool SetBonusData(const std::vector& bonus_data); + + bool operator==(const ImageChunk& other) const; + bool operator!=(const ImageChunk& other) const { + return !(*this == other); + } + + /* + * Cause a gzip chunk to be treated as a normal chunk (ie, as a blob of uninterpreted data). + * The resulting patch will likely be about as big as the target file, but it lets us handle + * the case of images where some gzip chunks are reconstructible but others aren't (by treating + * the ones that aren't as normal chunks). + */ + void ChangeDeflateChunkToNormal(); + + /* + * Verify that we can reproduce exactly the same compressed data that we started with. Sets the + * level, method, windowBits, memLevel, and strategy fields in the chunk to the encoding + * parameters needed to produce the right output. + */ + bool ReconstructDeflateChunk(); + bool IsAdjacentNormal(const ImageChunk& other) const; + void MergeAdjacentNormal(const ImageChunk& other); + + /* + * Compute a bsdiff patch between |src| and |tgt|; Store the result in the patch_data. + * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk is used + * repeatedly, pass nullptr if not needed. + */ + static bool MakePatch(const ImageChunk& tgt, const ImageChunk& src, + std::vector* patch_data, saidx_t** bsdiff_cache); + + private: + const uint8_t* GetRawData() const; + bool TryReconstruction(int level); + + int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW + size_t start_; // offset of chunk in the original input file + const std::vector* input_file_ptr_; // ptr to the full content of original input file + size_t raw_data_len_; + + // deflate encoder parameters + int compress_level_; + + // --- for CHUNK_DEFLATE chunks only: --- + std::vector uncompressed_data_; + std::string entry_name_; // used for zip entries +}; + +// PatchChunk stores the patch data between a source chunk and a target chunk. It also keeps track +// of the metadata of src&tgt chunks (e.g. offset, raw data length, uncompressed data length). +class PatchChunk { + public: + PatchChunk(const ImageChunk& tgt, const ImageChunk& src, std::vector data); + + // Construct a CHUNK_RAW patch from the target data directly. + explicit PatchChunk(const ImageChunk& tgt); + + // Return true if raw data size is smaller than the patch size. + static bool RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size); + + static bool WritePatchDataToFd(const std::vector& patch_chunks, int patch_fd); + + private: + size_t GetHeaderSize() const; + size_t WriteHeaderToFd(int fd, size_t offset) const; + + // The patch chunk type is the same as the target chunk type. The only exception is we change + // the |type_| to CHUNK_RAW if target length is smaller than the patch size. + int type_; + + size_t source_start_; + size_t source_len_; + size_t source_uncompressed_len_; + + size_t target_start_; // offset of the target chunk within the target file + size_t target_len_; + size_t target_uncompressed_len_; + size_t target_compress_level_; // the deflate compression level of the target chunk. + + std::vector data_; // storage for the patch data +}; + +// Interface for zip_mode and image_mode images. We initialize the image from an input file and +// split the file content into a list of image chunks. +class Image { + public: + explicit Image(bool is_source) : is_source_(is_source) {} + + virtual ~Image() {} + + // Create a list of image chunks from input file. + virtual bool Initialize(const std::string& filename) = 0; + + // Look for runs of adjacent normal chunks and compress them down into a single chunk. (Such + // runs can be produced when deflate chunks are changed to normal chunks.) + void MergeAdjacentNormalChunks(); + + void DumpChunks() const; + + // Non const iterators to access the stored ImageChunks. + std::vector::iterator begin() { + return chunks_.begin(); + } + + std::vector::iterator end() { + return chunks_.end(); + } + + ImageChunk& operator[](size_t i); + const ImageChunk& operator[](size_t i) const; + + size_t NumOfChunks() const { + return chunks_.size(); + } + + protected: + bool ReadFile(const std::string& filename, std::vector* file_content); + + bool is_source_; // True if it's for source chunks. + std::vector chunks_; // Internal storage of ImageChunk. + std::vector file_content_; // Store the whole input file in memory. +}; + +class ZipModeImage : public Image { + public: + explicit ZipModeImage(bool is_source) : Image(is_source) {} + + bool Initialize(const std::string& filename) override; + + // The pesudo source chunk for bsdiff if there's no match for the given target chunk. It's in + // fact the whole source file. + ImageChunk PseudoSource() const; + + // Find the matching deflate source chunk by entry name. Search for normal chunks also if + // |find_normal| is true. + ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false); + + const ImageChunk* FindChunkByName(const std::string& name, bool find_normal = false) const; + + // Verify that we can reconstruct the deflate chunks; also change the type to CHUNK_NORMAL if + // src and tgt are identical. + static bool CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* src_image); + + // Compute the patch between tgt & src images, and write the data into |patch_name|. + static bool GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image, + const std::string& patch_name); + + private: + // Initialize image chunks based on the zip entries. + bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle); + // Add the a zip entry to the list. + bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry); + // Return the real size of the zip file. (omit the trailing zeros that used for alignment) + bool GetZipFileSize(size_t* input_file_size); +}; + +class ImageModeImage : public Image { + public: + explicit ImageModeImage(bool is_source) : Image(is_source) {} + + // Initialize the image chunks list by searching the magic numbers in an image file. + bool Initialize(const std::string& filename) override; + + bool SetBonusData(const std::vector& bonus_data); + + // In Image Mode, verify that the source and target images have the same chunk structure (ie, the + // same sequence of deflate and normal chunks). + static bool CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeImage* src_image); + + // In image mode, generate patches against the given source chunks and bonus_data; write the + // result to |patch_name|. + static bool GeneratePatches(const ImageModeImage& tgt_image, const ImageModeImage& src_image, + const std::string& patch_name); +}; + +#endif // _APPLYPATCH_IMGDIFF_IMAGE_H diff --git a/rangeset.h b/rangeset.h new file mode 100644 index 000000000..f224a08be --- /dev/null +++ b/rangeset.h @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include + +using Range = std::pair; + +class RangeSet { + public: + RangeSet() : blocks_(0) {} + + explicit RangeSet(std::vector&& pairs) { + CHECK_NE(pairs.size(), static_cast(0)) << "Invalid number of tokens"; + + // Sanity check the input. + size_t result = 0; + for (const auto& range : pairs) { + CHECK_LT(range.first, range.second) + << "Empty or negative range: " << range.first << ", " << range.second; + size_t sz = range.second - range.first; + CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow"; + result += sz; + } + + ranges_ = pairs; + blocks_ = result; + } + + static RangeSet Parse(const std::string& range_text) { + std::vector pieces = android::base::Split(range_text, ","); + CHECK_GE(pieces.size(), static_cast(3)) << "Invalid range text: " << range_text; + + size_t num; + CHECK(android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) + << "Failed to parse the number of tokens: " << range_text; + + CHECK_NE(num, static_cast(0)) << "Invalid number of tokens: " << range_text; + CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; + CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; + + std::vector pairs; + for (size_t i = 0; i < num; i += 2) { + size_t first; + CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX))); + size_t second; + CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))); + + pairs.emplace_back(first, second); + } + + return RangeSet(std::move(pairs)); + } + + std::string ToString() const { + if (ranges_.empty()) { + return ""; + } + std::string result = std::to_string(ranges_.size() * 2); + for (const auto& r : ranges_) { + result += android::base::StringPrintf(",%zu,%zu", r.first, r.second); + } + + return result; + } + + // Get the block number for the i-th (starting from 0) block in the RangeSet. + size_t GetBlockNumber(size_t idx) const { + CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")"; + + for (const auto& range : ranges_) { + if (idx < range.second - range.first) { + return range.first + idx; + } + idx -= (range.second - range.first); + } + + CHECK(false) << "Failed to find block number for index " << idx; + return 0; // Unreachable, but to make compiler happy. + } + + // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" + // and "5,7" are not overlapped. + bool Overlaps(const RangeSet& other) const { + for (const auto& range : ranges_) { + size_t start = range.first; + size_t end = range.second; + for (const auto& other_range : other.ranges_) { + size_t other_start = other_range.first; + size_t other_end = other_range.second; + // [start, end) vs [other_start, other_end) + if (!(other_start >= end || start >= other_end)) { + return true; + } + } + } + return false; + } + + // size() gives the number of Range's in this RangeSet. + size_t size() const { + return ranges_.size(); + } + + // blocks() gives the number of all blocks in this RangeSet. + size_t blocks() const { + return blocks_; + } + + // We provide const iterators only. + std::vector::const_iterator cbegin() const { + return ranges_.cbegin(); + } + + std::vector::const_iterator cend() const { + return ranges_.cend(); + } + + // Need to provide begin()/end() since range-based loop expects begin()/end(). + std::vector::const_iterator begin() const { + return ranges_.cbegin(); + } + + std::vector::const_iterator end() const { + return ranges_.cend(); + } + + // Reverse const iterators for MoveRange(). + std::vector::const_reverse_iterator crbegin() const { + return ranges_.crbegin(); + } + + std::vector::const_reverse_iterator crend() const { + return ranges_.crend(); + } + + const Range& operator[](size_t i) const { + return ranges_[i]; + } + + bool operator==(const RangeSet& other) const { + // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5". + return (ranges_ == other.ranges_); + } + + bool operator!=(const RangeSet& other) const { + return ranges_ != other.ranges_; + } + + protected: + // Actual limit for each value and the total number are both INT_MAX. + std::vector ranges_; + size_t blocks_; +}; + +static constexpr size_t kBlockSize = 4096; + +// The class is a sorted version of a RangeSet; and it's useful in imgdiff to split the input +// files when we're handling large zip files. Specifically, we can treat the input file as a +// continuous RangeSet (i.e. RangeSet("0-99") for a 100 blocks file); and break it down into +// several smaller chunks based on the zip entries. + +// For example, [source: 0-99] can be split into +// [split_src1: 10-29]; [split_src2: 40-49, 60-69]; [split_src3: 70-89] +// Here "10-29" simply means block 10th to block 29th with respect to the original input file. +// Also, note that the split sources should be mutual exclusive, but they don't need to cover +// every block in the original source. +class SortedRangeSet : public RangeSet { + public: + SortedRangeSet() {} + + // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. + explicit SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { + std::sort(ranges_.begin(), ranges_.end()); + } + + void Insert(const Range& to_insert) { + SortedRangeSet rs({ to_insert }); + Insert(rs); + } + + // Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges. + void Insert(const SortedRangeSet& rs) { + if (rs.size() == 0) { + return; + } + // Merge and sort the two RangeSets. + std::vector temp = std::move(ranges_); + std::copy(rs.begin(), rs.end(), std::back_inserter(temp)); + std::sort(temp.begin(), temp.end()); + + Clear(); + // Trim overlaps and insert the result back to ranges_. + Range to_insert = temp.front(); + for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) { + if (it->first <= to_insert.second) { + to_insert.second = std::max(to_insert.second, it->second); + } else { + ranges_.push_back(to_insert); + blocks_ += (to_insert.second - to_insert.first); + to_insert = *it; + } + } + ranges_.push_back(to_insert); + blocks_ += (to_insert.second - to_insert.first); + } + + void Clear() { + blocks_ = 0; + ranges_.clear(); + } + + using RangeSet::Overlaps; + bool Overlaps(size_t start, size_t len) const { + RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } }); + return Overlaps(rs); + } + + // Compute the block range the file occupies, and insert that range. + void Insert(size_t start, size_t len) { + Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 }; + Insert(to_insert); + } + + // Given an offset of the file, checks if the corresponding block (by considering the file as + // 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset + // within this SortedRangeSet. + // + // For example, the 4106-th byte of a file is from block 1, assuming a block size of 4096-byte. + // The mapped offset within a SortedRangeSet("1-9 15-19") is 10. + // + // An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th + // item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10 + // + 10) in a range represented by this SortedRangeSet. + size_t GetOffsetInRangeSet(size_t old_offset) const { + size_t old_block_start = old_offset / kBlockSize; + size_t new_block_start = 0; + for (const auto& range : ranges_) { + // Find the index of old_block_start. + if (old_block_start >= range.second) { + new_block_start += (range.second - range.first); + } else if (old_block_start >= range.first) { + new_block_start += (old_block_start - range.first); + return (new_block_start * kBlockSize + old_offset % kBlockSize); + } else { + CHECK(false) << "block_start " << old_block_start + << " is missing between two ranges: " << this->ToString(); + return 0; + } + } + CHECK(false) << "block_start " << old_block_start + << " exceeds the limit of current RangeSet: " << this->ToString(); + return 0; + } +}; \ No newline at end of file diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp index 3993cb9ea..15bcec855 100644 --- a/tests/unit/rangeset_test.cpp +++ b/tests/unit/rangeset_test.cpp @@ -21,7 +21,7 @@ #include -#include "updater/rangeset.h" +#include "rangeset.h" TEST(RangeSetTest, Parse_smoke) { RangeSet rs = RangeSet::Parse("2,1,10"); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index a0b9ad233..fe21dd0eb 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -53,8 +53,8 @@ #include "error_code.h" #include "ota_io.h" #include "print_sha1.h" +#include "rangeset.h" #include "updater/install.h" -#include "updater/rangeset.h" #include "updater/updater.h" // Set this to 0 to interpret 'erase' transfers to mean do a diff --git a/updater/include/updater/rangeset.h b/updater/include/updater/rangeset.h deleted file mode 100644 index b67c98724..000000000 --- a/updater/include/updater/rangeset.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include -#include - -#include -#include -#include -#include - -using Range = std::pair; - -class RangeSet { - public: - RangeSet() : blocks_(0) {} - - explicit RangeSet(std::vector&& pairs) { - CHECK_NE(pairs.size(), static_cast(0)) << "Invalid number of tokens"; - - // Sanity check the input. - size_t result = 0; - for (const auto& range : pairs) { - CHECK_LT(range.first, range.second) - << "Empty or negative range: " << range.first << ", " << range.second; - size_t sz = range.second - range.first; - CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow"; - result += sz; - } - - ranges_ = pairs; - blocks_ = result; - } - - static RangeSet Parse(const std::string& range_text) { - std::vector pieces = android::base::Split(range_text, ","); - CHECK_GE(pieces.size(), static_cast(3)) << "Invalid range text: " << range_text; - - size_t num; - CHECK(android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) - << "Failed to parse the number of tokens: " << range_text; - - CHECK_NE(num, static_cast(0)) << "Invalid number of tokens: " << range_text; - CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; - CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; - - std::vector pairs; - for (size_t i = 0; i < num; i += 2) { - size_t first; - CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX))); - size_t second; - CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))); - - pairs.emplace_back(first, second); - } - - return RangeSet(std::move(pairs)); - } - - std::string ToString() const { - if (ranges_.empty()) { - return ""; - } - std::string result = std::to_string(ranges_.size() * 2); - for (const auto& r : ranges_) { - result += android::base::StringPrintf(",%zu,%zu", r.first, r.second); - } - - return result; - } - - // Get the block number for the i-th (starting from 0) block in the RangeSet. - size_t GetBlockNumber(size_t idx) const { - CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")"; - - for (const auto& range : ranges_) { - if (idx < range.second - range.first) { - return range.first + idx; - } - idx -= (range.second - range.first); - } - - CHECK(false) << "Failed to find block number for index " << idx; - return 0; // Unreachable, but to make compiler happy. - } - - // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" - // and "5,7" are not overlapped. - bool Overlaps(const RangeSet& other) const { - for (const auto& range : ranges_) { - size_t start = range.first; - size_t end = range.second; - for (const auto& other_range : other.ranges_) { - size_t other_start = other_range.first; - size_t other_end = other_range.second; - // [start, end) vs [other_start, other_end) - if (!(other_start >= end || start >= other_end)) { - return true; - } - } - } - return false; - } - - // size() gives the number of Range's in this RangeSet. - size_t size() const { - return ranges_.size(); - } - - // blocks() gives the number of all blocks in this RangeSet. - size_t blocks() const { - return blocks_; - } - - // We provide const iterators only. - std::vector::const_iterator cbegin() const { - return ranges_.cbegin(); - } - - std::vector::const_iterator cend() const { - return ranges_.cend(); - } - - // Need to provide begin()/end() since range-based loop expects begin()/end(). - std::vector::const_iterator begin() const { - return ranges_.cbegin(); - } - - std::vector::const_iterator end() const { - return ranges_.cend(); - } - - // Reverse const iterators for MoveRange(). - std::vector::const_reverse_iterator crbegin() const { - return ranges_.crbegin(); - } - - std::vector::const_reverse_iterator crend() const { - return ranges_.crend(); - } - - const Range& operator[](size_t i) const { - return ranges_[i]; - } - - bool operator==(const RangeSet& other) const { - // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5". - return (ranges_ == other.ranges_); - } - - bool operator!=(const RangeSet& other) const { - return ranges_ != other.ranges_; - } - - protected: - // Actual limit for each value and the total number are both INT_MAX. - std::vector ranges_; - size_t blocks_; -}; - -static constexpr size_t kBlockSize = 4096; - -// The class is a sorted version of a RangeSet; and it's useful in imgdiff to split the input -// files when we're handling large zip files. Specifically, we can treat the input file as a -// continuous RangeSet (i.e. RangeSet("0-99") for a 100 blocks file); and break it down into -// several smaller chunks based on the zip entries. - -// For example, [source: 0-99] can be split into -// [split_src1: 10-29]; [split_src2: 40-49, 60-69]; [split_src3: 70-89] -// Here "10-29" simply means block 10th to block 29th with respect to the original input file. -// Also, note that the split sources should be mutual exclusive, but they don't need to cover -// every block in the original source. -class SortedRangeSet : public RangeSet { - public: - SortedRangeSet() {} - - // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. - explicit SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { - std::sort(ranges_.begin(), ranges_.end()); - } - - void Insert(const Range& to_insert) { - SortedRangeSet rs({ to_insert }); - Insert(rs); - } - - // Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges. - void Insert(const SortedRangeSet& rs) { - if (rs.size() == 0) { - return; - } - // Merge and sort the two RangeSets. - std::vector temp = std::move(ranges_); - std::copy(rs.begin(), rs.end(), std::back_inserter(temp)); - std::sort(temp.begin(), temp.end()); - - Clear(); - // Trim overlaps and insert the result back to ranges_. - Range to_insert = temp.front(); - for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) { - if (it->first <= to_insert.second) { - to_insert.second = std::max(to_insert.second, it->second); - } else { - ranges_.push_back(to_insert); - blocks_ += (to_insert.second - to_insert.first); - to_insert = *it; - } - } - ranges_.push_back(to_insert); - blocks_ += (to_insert.second - to_insert.first); - } - - void Clear() { - blocks_ = 0; - ranges_.clear(); - } - - using RangeSet::Overlaps; - bool Overlaps(size_t start, size_t len) const { - RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } }); - return Overlaps(rs); - } - - // Compute the block range the file occupies, and insert that range. - void Insert(size_t start, size_t len) { - Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 }; - Insert(to_insert); - } - - // Given an offset of the file, checks if the corresponding block (by considering the file as - // 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset - // within this SortedRangeSet. - // - // For example, the 4106-th byte of a file is from block 1, assuming a block size of 4096-byte. - // The mapped offset within a SortedRangeSet("1-9 15-19") is 10. - // - // An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th - // item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10 - // + 10) in a range represented by this SortedRangeSet. - size_t GetOffsetInRangeSet(size_t old_offset) const { - size_t old_block_start = old_offset / kBlockSize; - size_t new_block_start = 0; - for (const auto& range : ranges_) { - // Find the index of old_block_start. - if (old_block_start >= range.second) { - new_block_start += (range.second - range.first); - } else if (old_block_start >= range.first) { - new_block_start += (old_block_start - range.first); - return (new_block_start * kBlockSize + old_offset % kBlockSize); - } else { - CHECK(false) <<"block_start " << old_block_start << " is missing between two ranges: " - << this->ToString(); - return 0; - } - } - CHECK(false) <<"block_start " << old_block_start << " exceeds the limit of current RangeSet: " - << this->ToString(); - return 0; - } -}; \ No newline at end of file -- cgit v1.2.3 From 69b9649e9bfafc2ddb5aa4dd609895b1fc097364 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 17 Aug 2017 16:42:57 -0700 Subject: Allow comparison against multi serial nums for A/B package The metadata file now can have multiple serial numbers in the format: serialno=serialno1|serialno2|serialno3 ... Verifier will pass the check if the device serial number matches any of these numbers. Bug: 64802465 Test: Create a metadata file with 1000 numbers and sideload in sailfish. The checker detects both match and mismatch cases. Change-Id: I3f12b75e15f4179df260778e37f4563d65db0fa8 --- install.cpp | 20 ++++++++--- tests/component/install_test.cpp | 71 ++++++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 18 deletions(-) diff --git a/install.cpp b/install.cpp index 7fbf5c01f..1220c6ab7 100644 --- a/install.cpp +++ b/install.cpp @@ -148,13 +148,23 @@ static int check_newer_ab_build(ZipArchiveHandle zip) { return INSTALL_ERROR; } - // We allow the package to not have any serialno, but if it has a non-empty - // value it should match. + // We allow the package to not have any serialno; and we also allow it to carry multiple serial + // numbers split by "|"; e.g. serialno=serialno1|serialno2|serialno3 ... We will fail the + // verification if the device's serialno doesn't match any of these carried numbers. value = android::base::GetProperty("ro.serialno", ""); const std::string& pkg_serial_no = metadata["serialno"]; - if (!pkg_serial_no.empty() && pkg_serial_no != value) { - LOG(ERROR) << "Package is for serial " << pkg_serial_no; - return INSTALL_ERROR; + if (!pkg_serial_no.empty()) { + bool match = false; + for (const std::string& number : android::base::Split(pkg_serial_no, "|")) { + if (value == android::base::Trim(number)) { + match = true; + break; + } + } + if (!match) { + LOG(ERROR) << "Package is for serial " << pkg_serial_no; + return INSTALL_ERROR; + } } if (metadata["ota-type"] != "AB") { diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 968196fc0..7bb496066 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -198,8 +199,8 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml CloseArchive(zip); } -TEST(InstallTest, update_binary_command_smoke) { #ifdef AB_OTA_UPDATER +static void VerifyAbUpdateBinaryCommand(const std::string& serialno, bool success = true) { TemporaryFile temp_file; FILE* zip_file = fdopen(temp_file.fd, "w"); ZipWriter writer(zip_file); @@ -215,11 +216,13 @@ TEST(InstallTest, update_binary_command_smoke) { ASSERT_NE("", device); std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); ASSERT_NE("", timestamp); - std::string metadata = android::base::Join( - std::vector{ - "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp, - }, - "\n"); + + std::vector meta{ "ota-type=AB", "pre-device=" + device, + "post-timestamp=" + timestamp }; + if (!serialno.empty()) { + meta.push_back("serialno=" + serialno); + } + std::string metadata = android::base::Join(meta, "\n"); ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size())); ASSERT_EQ(0, writer.FinishEntry()); ASSERT_EQ(0, writer.Finish()); @@ -234,14 +237,25 @@ TEST(InstallTest, update_binary_command_smoke) { std::string package = "/path/to/update.zip"; std::string binary_path = "/sbin/update_engine_sideload"; std::vector cmd; - ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); - ASSERT_EQ(5U, cmd.size()); - ASSERT_EQ(binary_path, cmd[0]); - ASSERT_EQ("--payload=file://" + package, cmd[1]); - ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]); - ASSERT_EQ("--headers=" + properties, cmd[3]); - ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); + if (success) { + ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); + ASSERT_EQ(5U, cmd.size()); + ASSERT_EQ(binary_path, cmd[0]); + ASSERT_EQ("--payload=file://" + package, cmd[1]); + ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]); + ASSERT_EQ("--headers=" + properties, cmd[3]); + ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); + } else { + ASSERT_EQ(INSTALL_ERROR, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); + } CloseArchive(zip); +} +#endif // AB_OTA_UPDATER + +TEST(InstallTest, update_binary_command_smoke) { +#ifdef AB_OTA_UPDATER + // Empty serialno will pass the verification. + VerifyAbUpdateBinaryCommand({}); #else TemporaryFile temp_file; FILE* zip_file = fdopen(temp_file.fd, "w"); @@ -340,3 +354,34 @@ TEST(InstallTest, update_binary_command_invalid) { CloseArchive(zip); #endif // AB_OTA_UPDATER } + +#ifdef AB_OTA_UPDATER +TEST(InstallTest, update_binary_command_multiple_serialno) { + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + + // Single matching serialno will pass the verification. + VerifyAbUpdateBinaryCommand(serialno); + + static constexpr char alphabet[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + auto generator = []() { return alphabet[rand() % (sizeof(alphabet) - 1)]; }; + + // Generate 900 random serial numbers. + std::string random_serial; + for (size_t i = 0; i < 900; i++) { + generate_n(back_inserter(random_serial), serialno.size(), generator); + random_serial.append("|"); + } + // Random serialnos should fail the verification. + VerifyAbUpdateBinaryCommand(random_serial, false); + + std::string long_serial = random_serial + serialno + "|"; + for (size_t i = 0; i < 99; i++) { + generate_n(back_inserter(long_serial), serialno.size(), generator); + long_serial.append("|"); + } + // String with the matching serialno should pass the verification. + VerifyAbUpdateBinaryCommand(long_serial); +} +#endif // AB_OTA_UPDATER -- cgit v1.2.3 From 3fec8c64190b6600c01f383501f55f3b9350c213 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 23 Aug 2017 00:18:07 -0700 Subject: Add missing report of error code under recovery Add report of kMapFileFailure, kForkUpdateBinaryFailure and kUpdateBinaryCommandFailure. Test: unit test pass; kMapFileFailure reports correctly. Change-Id: I858c81b6c750b5e300b1b12760b1f0fa8e23b47d --- error_code.h | 3 +++ install.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/error_code.h b/error_code.h index 4cbad4cfe..4e3032bc9 100644 --- a/error_code.h +++ b/error_code.h @@ -25,6 +25,9 @@ enum ErrorCode { kBootreasonInBlacklist, kPackageCompatibilityFailure, kScriptExecutionFailure, + kMapFileFailure, + kForkUpdateBinaryFailure, + kUpdateBinaryCommandFailure, }; enum CauseCode { diff --git a/install.cpp b/install.cpp index 7fbf5c01f..80ef71541 100644 --- a/install.cpp +++ b/install.cpp @@ -321,6 +321,7 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b if (ret) { close(pipefd[0]); close(pipefd[1]); + log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); return ret; } @@ -385,6 +386,7 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b close(pipefd[0]); close(pipefd[1]); PLOG(ERROR) << "Failed to fork update binary"; + log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure)); return INSTALL_ERROR; } @@ -573,6 +575,7 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo MemMapping map; if (!map.MapFile(path)) { LOG(ERROR) << "failed to map file"; + log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure)); return INSTALL_CORRUPT; } -- cgit v1.2.3 From 7b795abd13ba11280920c6b39174d2b02b014970 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 23 Aug 2017 19:34:04 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I8fb3f973ecd0dfe54dea048ef445d45902eae028 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- tools/recovery_l10n/res/values-hi/strings.xml | 2 +- tools/recovery_l10n/res/values-mr/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/recovery_l10n/res/values-hi/strings.xml b/tools/recovery_l10n/res/values-hi/strings.xml index a8a876ee4..65d003352 100644 --- a/tools/recovery_l10n/res/values-hi/strings.xml +++ b/tools/recovery_l10n/res/values-hi/strings.xml @@ -3,7 +3,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "सिस्टम अपडेट इंस्टॉल किया जा रहा है" "मिटाया जा रहा है" - "कोई आदेश नहीं" + "कोई निर्देश नहीं मिला" "गड़बड़ी!" "सुरक्षा अपडेट इंस्टॉल किया जा रहा है" diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml index 017a515c0..a5eff84c7 100644 --- a/tools/recovery_l10n/res/values-mr/strings.xml +++ b/tools/recovery_l10n/res/values-mr/strings.xml @@ -3,7 +3,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "सिस्टम अपडेट इंस्टॉल करत आहे" "मिटवत आहे" - "कोणताही आदेश नाही" + "कोणतीही कमांड नाही" "त्रुटी!" "सुरक्षा अपडेट इंस्टॉल करत आहे" -- cgit v1.2.3 From cff8269e5c364ed48d4f3b81b7f67251ef73a81e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sat, 26 Aug 2017 07:56:44 -0700 Subject: wear_ui: Remove dead 'self' and 'progress_t'. They were once used for progress_thread() that's pthread_create'd by WearRecoveryUI::Init(). They have become dead since the removal of progress_thread() (commit ad8b5a6c1195b94d8d80671e1bf791c32008fbef). Also add the missing include of for pthread_mutex_lock(). Test: lunch a watch target and `m recoveryimage`. Change-Id: I748cf4511434ac4ce97dddf89b0e42e68a5da04b --- wear_ui.cpp | 8 +------- wear_ui.h | 2 -- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/wear_ui.cpp b/wear_ui.cpp index 169ef20e1..75d01bdc3 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -38,11 +39,6 @@ #include "common.h" #include "device.h" -// There's only (at most) one of these objects, and global callbacks -// (for pthread_create, and the input event system) need to find it, -// so use a global variable. -static WearRecoveryUI* self = NULL; - // Return the current time as a double (including fractions of a second). static double now() { struct timeval tv; @@ -63,8 +59,6 @@ WearRecoveryUI::WearRecoveryUI() touch_screen_allowed_ = true; for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL; - - self = this; } int WearRecoveryUI::GetProgressBaseline() const { diff --git a/wear_ui.h b/wear_ui.h index 3bd90b699..d04718ccf 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -68,8 +68,6 @@ class WearRecoveryUI : public ScreenRecoveryUI { const char* const* menu_headers_; int menu_start, menu_end; - pthread_t progress_t; - void draw_background_locked() override; void draw_screen_locked() override; -- cgit v1.2.3 From 1e27d14eea7c11eb099e0e05dd0e26a49f21a13b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sat, 26 Aug 2017 08:32:29 -0700 Subject: wear_ui: Remove kMaxCols/kMaxRows/visible_text_rows/menu_headers_. We've deprecated kMaxCols/kMaxRows in ScreenRecoveryUI since commit aa0d6afb61f4cf928e87c7a21bcb59fc973f15a0. They unnecessarily limit the screen size to 96x96 characters. Also remove the recomputations of text_cols, visible_text_rows (which is the same as text_rows_). Remove the unintentional hiding of menu_headers_. Test: mmma bootable/recovery Change-Id: I7d1526b651943312d62d52cd200414b42bf9b12a --- wear_ui.cpp | 16 +--------------- wear_ui.h | 9 --------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/wear_ui.cpp b/wear_ui.cpp index 169ef20e1..71a535199 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -191,20 +191,6 @@ void WearRecoveryUI::update_progress_locked() { gr_flip(); } -bool WearRecoveryUI::InitTextParams() { - if (!ScreenRecoveryUI::InitTextParams()) { - return false; - } - - text_cols_ = (gr_fb_width() - (kMarginWidth * 2)) / char_width_; - - if (text_rows_ > kMaxRows) text_rows_ = kMaxRows; - if (text_cols_ > kMaxCols) text_cols_ = kMaxCols; - - visible_text_rows = (gr_fb_height() - (kMarginHeight * 2)) / char_height_; - return true; -} - bool WearRecoveryUI::Init(const std::string& locale) { if (!ScreenRecoveryUI::Init(locale)) { return false; @@ -269,7 +255,7 @@ void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* it show_menu = true; menu_sel = initial_selection; menu_start = 0; - menu_end = visible_text_rows - 1 - kMenuUnusableRows; + menu_end = text_rows_ - 1 - kMenuUnusableRows; if (menu_items <= menu_end) menu_end = menu_items; update_screen_locked(); } diff --git a/wear_ui.h b/wear_ui.h index 3bd90b699..2c5b7e481 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -50,8 +50,6 @@ class WearRecoveryUI : public ScreenRecoveryUI { int GetProgressBaseline() const override; - bool InitTextParams() override; - void update_progress_locked() override; void PrintV(const char*, bool, va_list) override; @@ -59,13 +57,6 @@ class WearRecoveryUI : public ScreenRecoveryUI { private: GRSurface* backgroundIcon[5]; - static const int kMaxCols = 96; - static const int kMaxRows = 96; - - // Number of text rows seen on screen - int visible_text_rows; - - const char* const* menu_headers_; int menu_start, menu_end; pthread_t progress_t; -- cgit v1.2.3 From 9227a5ecec12a4b74c2986b023649f7b1fc24b20 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 27 Aug 2017 11:10:05 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I0b2ff87788553d2c49519d005f8876cd7318b8c6 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-mr/strings.xml | 2 +- tools/recovery_l10n/res/values-te/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml index a5eff84c7..5f820336f 100644 --- a/tools/recovery_l10n/res/values-mr/strings.xml +++ b/tools/recovery_l10n/res/values-mr/strings.xml @@ -4,6 +4,6 @@ "सिस्टम अपडेट इंस्टॉल करत आहे" "मिटवत आहे" "कोणतीही कमांड नाही" - "त्रुटी!" + "एरर!" "सुरक्षा अपडेट इंस्टॉल करत आहे" diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml index cfb02c915..e35c82bc4 100644 --- a/tools/recovery_l10n/res/values-te/strings.xml +++ b/tools/recovery_l10n/res/values-te/strings.xml @@ -4,6 +4,6 @@ "సిస్టమ్ నవీకరణను ఇన్‍స్టాల్ చేస్తోంది" "డేటాను తొలగిస్తోంది" "ఆదేశం లేదు" - "లోపం సంభవించింది!" + "ఎర్రర్ సంభవించింది!" "భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది" -- cgit v1.2.3 From c89d1e7e2a6a8a07bb336db35db88f5452d8a51d Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 28 Aug 2017 14:15:07 -0700 Subject: Turn on -Wall for recovery modules Turn on -Wall for all modules. Also remove the obsolete file_cmp() in apply_patch test and now() in wear_ui. The only exception is lib_edify due to the unused functions in the intermediate cpp files generated from the lex files. It will be handled in a seperate CL. Bug: 64939312 Test: mma, unit tests pass Change-Id: Ic53f76b60b6401ab20db3d98130d674c08e3702f --- Android.mk | 10 ++++++---- applypatch/Android.mk | 10 +++++++--- bootloader_message/Android.bp | 5 ++++- minui/Android.mk | 4 ++-- otafault/Android.mk | 3 ++- otafault/config.cpp | 4 +++- print_sha1.h | 6 +++--- tests/Android.mk | 7 ++++--- tests/component/applypatch_test.cpp | 8 -------- uncrypt/Android.mk | 2 +- updater/Android.mk | 2 ++ wear_ui.cpp | 8 -------- 12 files changed, 34 insertions(+), 35 deletions(-) diff --git a/Android.mk b/Android.mk index 776e6ea19..b8e5837ef 100644 --- a/Android.mk +++ b/Android.mk @@ -93,7 +93,7 @@ endif endif LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) -LOCAL_CFLAGS += -Wno-unused-parameter -Werror +LOCAL_CFLAGS += -Wall -Wno-unused-parameter -Werror ifneq ($(TARGET_RECOVERY_UI_MARGIN_HEIGHT),) LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=$(TARGET_RECOVERY_UI_MARGIN_HEIGHT) @@ -203,7 +203,7 @@ LOCAL_SRC_FILES := \ rotate_logs.cpp LOCAL_MODULE := recovery-persist LOCAL_SHARED_LIBRARIES := liblog libbase -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_INIT_RC := recovery-persist.rc include $(BUILD_EXECUTABLE) @@ -215,7 +215,7 @@ LOCAL_SRC_FILES := \ rotate_logs.cpp LOCAL_MODULE := recovery-refresh LOCAL_SHARED_LIBRARIES := liblog libbase -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_INIT_RC := recovery-refresh.rc include $(BUILD_EXECUTABLE) @@ -230,13 +230,14 @@ LOCAL_STATIC_LIBRARIES := \ libcrypto_utils \ libcrypto \ libbase -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror include $(BUILD_STATIC_LIBRARY) # Wear default device # =============================== include $(CLEAR_VARS) LOCAL_SRC_FILES := wear_device.cpp +LOCAL_CFLAGS := -Wall -Werror # Should match TARGET_RECOVERY_UI_LIB in BoardConfig.mk. LOCAL_MODULE := librecovery_ui_wear @@ -248,6 +249,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := vr_device.cpp +LOCAL_CFLAGS := -Wall -Werror # should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk LOCAL_MODULE := librecovery_ui_vr diff --git a/applypatch/Android.mk b/applypatch/Android.mk index e38207c22..59aa0ce6c 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -37,6 +37,7 @@ LOCAL_STATIC_LIBRARIES := \ libz LOCAL_CFLAGS := \ -DZLIB_CONST \ + -Wall \ -Werror include $(BUILD_STATIC_LIBRARY) @@ -59,6 +60,7 @@ LOCAL_STATIC_LIBRARIES := \ libz LOCAL_CFLAGS := \ -DZLIB_CONST \ + -Wall \ -Werror include $(BUILD_STATIC_LIBRARY) @@ -82,6 +84,7 @@ LOCAL_STATIC_LIBRARIES := \ libz LOCAL_CFLAGS := \ -DZLIB_CONST \ + -Wall \ -Werror include $(BUILD_HOST_STATIC_LIBRARY) @@ -97,7 +100,7 @@ LOCAL_STATIC_LIBRARIES := \ libbase \ libedify \ libcrypto -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror include $(BUILD_STATIC_LIBRARY) # applypatch (target executable) @@ -119,13 +122,14 @@ LOCAL_SHARED_LIBRARIES := \ libbase \ libz \ libcutils -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror include $(BUILD_EXECUTABLE) libimgdiff_src_files := imgdiff.cpp # libbsdiff is compiled with -D_FILE_OFFSET_BITS=64. libimgdiff_cflags := \ + -Wall \ -Werror \ -D_FILE_OFFSET_BITS=64 \ -DZLIB_CONST @@ -177,7 +181,7 @@ include $(BUILD_HOST_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := imgdiff_main.cpp LOCAL_MODULE := imgdiff -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_STATIC_LIBRARIES := \ libimgdiff \ $(libimgdiff_static_libraries) \ diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index f0d76e718..456b04c33 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -17,7 +17,10 @@ cc_library_static { name: "libbootloader_message", srcs: ["bootloader_message.cpp"], - cppflags: ["-Werror"], + cppflags: [ + "-Wall", + "-Werror", + ], static_libs: [ "libbase", "libfs_mgr", diff --git a/minui/Android.mk b/minui/Android.mk index 4dfc65f8a..924698495 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -32,7 +32,7 @@ LOCAL_STATIC_LIBRARIES := \ libpng \ libbase -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include @@ -68,7 +68,7 @@ LOCAL_SHARED_LIBRARIES := \ libpng \ libbase -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_SHARED_LIBRARY) diff --git a/otafault/Android.mk b/otafault/Android.mk index 7b5aab0b8..4784d56ef 100644 --- a/otafault/Android.mk +++ b/otafault/Android.mk @@ -24,6 +24,7 @@ otafault_static_libs := \ liblog LOCAL_CFLAGS := \ + -Wall \ -Werror \ -Wthread-safety \ -Wthread-safety-negative \ @@ -46,7 +47,7 @@ LOCAL_SRC_FILES := config.cpp ota_io.cpp test.cpp LOCAL_MODULE_TAGS := tests LOCAL_MODULE := otafault_test LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_C_INCLUDES := bootable/recovery LOCAL_FORCE_STATIC_EXECUTABLE := true diff --git a/otafault/config.cpp b/otafault/config.cpp index 8590833ee..b94e429c6 100644 --- a/otafault/config.cpp +++ b/otafault/config.cpp @@ -69,7 +69,9 @@ std::string fault_fname(const char* io_type) { fname.resize(OTAIO_MAX_FNAME_SIZE); ZipString zip_type_path(type_path.c_str()); ZipEntry entry; - int status = FindEntry(archive, zip_type_path, &entry); + if (FindEntry(archive, zip_type_path, &entry) != 0) { + return {}; + } ExtractToMemory(archive, &entry, reinterpret_cast(&fname[0]), OTAIO_MAX_FNAME_SIZE); return fname; } diff --git a/print_sha1.h b/print_sha1.h index 1f8589519..d0c18b355 100644 --- a/print_sha1.h +++ b/print_sha1.h @@ -32,15 +32,15 @@ static std::string print_sha1(const uint8_t* sha1, size_t len) { return result; } -static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { +[[maybe_unused]] static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { return print_sha1(sha1, SHA_DIGEST_LENGTH); } -static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { +[[maybe_unused]] static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { return print_sha1(sha1, 4); } -static std::string print_hex(const uint8_t* bytes, size_t len) { +[[maybe_unused]] static std::string print_hex(const uint8_t* bytes, size_t len) { return print_sha1(bytes, len); } diff --git a/tests/Android.mk b/tests/Android.mk index f2497b8b3..748d9c87b 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH := $(call my-dir) # Unit tests include $(CLEAR_VARS) -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE := recovery_unit_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_STATIC_LIBRARIES := \ @@ -46,7 +46,7 @@ include $(BUILD_NATIVE_TEST) # Manual tests include $(CLEAR_VARS) -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE := recovery_manual_test LOCAL_STATIC_LIBRARIES := \ libminui \ @@ -81,6 +81,7 @@ include $(BUILD_NATIVE_TEST) # Component tests include $(CLEAR_VARS) LOCAL_CFLAGS := \ + -Wall \ -Werror \ -D_FILE_OFFSET_BITS=64 @@ -191,7 +192,7 @@ include $(BUILD_NATIVE_TEST) # Host tests include $(CLEAR_VARS) -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE := recovery_host_test LOCAL_MODULE_HOST_OS := linux LOCAL_C_INCLUDES := bootable/recovery diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 016fed9b1..42542898b 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -61,14 +61,6 @@ static void mangle_file(const std::string& fname) { ASSERT_TRUE(android::base::WriteStringToFile(content, fname)); } -static bool file_cmp(const std::string& f1, const std::string& f2) { - std::string c1; - android::base::ReadFileToString(f1, &c1); - std::string c2; - android::base::ReadFileToString(f2, &c2); - return c1 == c2; -} - class ApplyPatchTest : public ::testing::Test { public: static void SetUpTestCase() { diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index cb60c721e..a3b0ca98d 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -25,7 +25,7 @@ LOCAL_STATIC_LIBRARIES := \ liblog \ libfs_mgr \ libcutils -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_INIT_RC := uncrypt.rc include $(BUILD_EXECUTABLE) diff --git a/updater/Android.mk b/updater/Android.mk index 86dc48e30..12181602f 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -66,6 +66,7 @@ LOCAL_C_INCLUDES := \ external/e2fsprogs/misc LOCAL_CFLAGS := \ + -Wall \ -Wno-unused-parameter \ -Werror @@ -91,6 +92,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include LOCAL_CFLAGS := \ + -Wall \ -Wno-unused-parameter \ -Werror diff --git a/wear_ui.cpp b/wear_ui.cpp index e4806718d..85c8f835d 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -39,13 +39,6 @@ #include "common.h" #include "device.h" -// Return the current time as a double (including fractions of a second). -static double now() { - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec + tv.tv_usec / 1000000.0; -} - WearRecoveryUI::WearRecoveryUI() : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), kMenuUnusableRows(RECOVERY_UI_MENU_UNUSABLE_ROWS) { @@ -167,7 +160,6 @@ void WearRecoveryUI::draw_screen_locked() { // display from the bottom up, until we hit the top of the // screen, the bottom of the menu, or we've displayed the // entire text buffer. - int ty; int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; for (int ty = gr_fb_height() - char_height_ - kMarginHeight; ty > y + 2 && count < text_rows_; -- cgit v1.2.3 From 79127102e40fde2669db7136f40f77ab6727c386 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 30 Aug 2017 15:23:34 -0700 Subject: wear_ui: Remove backgroundIcon. It's covered by the equivalent variable in ScreenRecoveryUI: GRSurface* error_icon; Also refactor WearRecoveryUI::draw_background_locked() to get it closer to ScreenRecoveryUI code. Test: Build a wearable target recovery; Run graphics test. Change-Id: I3a8d0e4dbf6fe170e4f3adde7eaf4a2043132a57 --- wear_ui.cpp | 40 ++++++---------------------------------- wear_ui.h | 8 ++------ 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/wear_ui.cpp b/wear_ui.cpp index 85c8f835d..b9289d366 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -50,8 +50,6 @@ WearRecoveryUI::WearRecoveryUI() loop_frames = 60; touch_screen_allowed_ = true; - - for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL; } int WearRecoveryUI::GetProgressBaseline() const { @@ -67,24 +65,12 @@ void WearRecoveryUI::draw_background_locked() { gr_fill(0, 0, gr_fb_width(), gr_fb_height()); if (currentIcon != NONE) { - GRSurface* surface; - if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { - if (!intro_done) { - surface = introFrames[current_frame]; - } else { - surface = loopFrames[current_frame]; - } - } else { - surface = backgroundIcon[currentIcon]; - } - - int width = gr_get_width(surface); - int height = gr_get_height(surface); - - int x = (gr_fb_width() - width) / 2; - int y = (gr_fb_height() - height) / 2; - - gr_blit(surface, 0, 0, width, height, x, y); + GRSurface* frame = GetCurrentFrame(); + int frame_width = gr_get_width(frame); + int frame_height = gr_get_height(frame); + int frame_x = (gr_fb_width() - frame_width) / 2; + int frame_y = (gr_fb_height() - frame_height) / 2; + gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); } } @@ -177,20 +163,6 @@ void WearRecoveryUI::update_progress_locked() { gr_flip(); } -bool WearRecoveryUI::Init(const std::string& locale) { - if (!ScreenRecoveryUI::Init(locale)) { - return false; - } - - LoadBitmap("icon_error", &backgroundIcon[ERROR]); - backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; - - // This leaves backgroundIcon[INSTALLING_UPDATE] and backgroundIcon[ERASING] - // as NULL which is fine since draw_background_locked() doesn't use them. - - return true; -} - void WearRecoveryUI::SetStage(int current, int max) { } diff --git a/wear_ui.h b/wear_ui.h index 9731f4161..e97598bb1 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -25,8 +25,6 @@ class WearRecoveryUI : public ScreenRecoveryUI { public: WearRecoveryUI(); - bool Init(const std::string& locale) override; - void SetStage(int current, int max) override; // printing messages @@ -55,14 +53,12 @@ class WearRecoveryUI : public ScreenRecoveryUI { void PrintV(const char*, bool, va_list) override; private: - GRSurface* backgroundIcon[5]; - - int menu_start, menu_end; - void draw_background_locked() override; void draw_screen_locked() override; void PutChar(char); + + int menu_start, menu_end; }; #endif // RECOVERY_WEAR_UI_H -- cgit v1.2.3 From c7cd918c156c752cea22b315a1f53363bf264975 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 29 Aug 2017 12:06:02 -0700 Subject: Turn on -Wall for libedify Bug: 64939312 Test: mma Change-Id: Ia4afc6241b1f11ce261be6840f21a793b23014a6 --- edify/Android.mk | 4 ++-- edify/lexer.ll | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/edify/Android.mk b/edify/Android.mk index ffd54c208..baf4dd21d 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -28,7 +28,7 @@ LOCAL_SRC_FILES := \ $(edify_src_files) \ edify_parser.cpp -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_CPPFLAGS := -g -O0 LOCAL_MODULE := edify_parser LOCAL_YACCFLAGS := -v @@ -46,7 +46,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(edify_src_files) -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_CPPFLAGS := -Wno-unused-parameter LOCAL_CPPFLAGS += -Wno-deprecated-register LOCAL_MODULE := libedify diff --git a/edify/lexer.ll b/edify/lexer.ll index b764d1699..cb4594371 100644 --- a/edify/lexer.ll +++ b/edify/lexer.ll @@ -35,6 +35,8 @@ std::string string_buffer; %x STR +%option noinput +%option nounput %option noyywrap %% -- cgit v1.2.3 From ee8a96a581f60acfb92d507c9394bb6914606ee4 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 1 Sep 2017 11:37:50 -0700 Subject: wear_ui: Remove PrintOnScreenOnly() and PrintV(). They're just copy/pastes from the base class (ScreenRecoveryUI). Test: mmma bootable/recovery Change-Id: I341416107a14d89d3366bba757da0b1abb988b15 --- wear_ui.cpp | 34 +--------------------------------- wear_ui.h | 3 --- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/wear_ui.cpp b/wear_ui.cpp index b9289d366..670050a0b 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -32,8 +32,8 @@ #include #include -#include #include +#include #include #include "common.h" @@ -312,35 +312,3 @@ void WearRecoveryUI::ShowFile(const char* filename) { ShowFile(fp); fclose(fp); } - -void WearRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - PrintV(fmt, false, ap); - va_end(ap); -} - -void WearRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) { - std::string str; - android::base::StringAppendV(&str, fmt, ap); - - if (copy_to_stdout) { - fputs(str.c_str(), stdout); - } - - pthread_mutex_lock(&updateMutex); - if (text_rows_ > 0 && text_cols_ > 0) { - for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) { - if (*ptr == '\n' || text_col_ >= text_cols_) { - text_[text_row_][text_col_] = '\0'; - text_col_ = 0; - text_row_ = (text_row_ + 1) % text_rows_; - if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; - } - if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; - } - text_[text_row_][text_col_] = '\0'; - update_screen_locked(); - } - pthread_mutex_unlock(&updateMutex); -} diff --git a/wear_ui.h b/wear_ui.h index e97598bb1..92ef813f3 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -29,7 +29,6 @@ class WearRecoveryUI : public ScreenRecoveryUI { // printing messages void Print(const char* fmt, ...) override; - void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3); void ShowFile(const char* filename) override; void ShowFile(FILE* fp) override; @@ -50,8 +49,6 @@ class WearRecoveryUI : public ScreenRecoveryUI { void update_progress_locked() override; - void PrintV(const char*, bool, va_list) override; - private: void draw_background_locked() override; void draw_screen_locked() override; -- cgit v1.2.3 From 2903cddb58f6ee99116e0751a2305f75f9a86461 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 18 Aug 2017 18:15:47 -0700 Subject: Improve imgdiff for large zip files Due to the cache size limit for OTA generation, we used to split large zip files linearly into pieces and do bsdiff on them. As a result, i) we lose the advantage of imgdiff; ii) if there's an accidental order change of some huge files inside the zip, we'll create an insanely large patch. This patch splits the src&tgt more smartly based on the zip entry_name. If the entry_name is empty or no matching source is found for a target chunk, we'll skip adding its source and later do a bsdiff against the whole split source image (this rarely happens in our use cases except for the metadata inside a ziparchive). After the split, the target pieces are continuous and block aligned, while the sources pieces are mutually exclusive. (Some of the source blocks may not be used if there's no matching entry_name in the target.) Then we will generate patches accordingly between each split image pairs. Afterwards, if we apply imgpatch to each pair of split source/target images and add up the patched result, we can get back the original target image. For example: Input: [src_image, tgt_image] Split: [src-0,tgt-0; src-1,tgt-1, src-2,tgt-2] Diff: [ patch-0; patch-1; patch-2] Patch: [(src-0,patch-0)=tgt-0; (src-1,patch-1)=tgt-1; (src-2,patch-2)=tgt-2;] Append: [tgt-0 + tgt-1 + tgt-2 = tgt_image] Peformance: For the small package in b/34220646, we decrease the patch size of chrome.apk dramatically from 30M to 400K due to the order change of two big .so files. On two versions of angler, I also observe decent patch size decrease. For chrome.apk, we reduced the size from 5.9M to 3.2M; and for vevlet.apk from 8.0M to 6.5M. Bug: 34220646 Test: recovery component test && apply imgdiff & imgpatch on two chrome.apk Change-Id: I145d802984fa805efbbac9d01a2e64d82ef9728b --- applypatch/imgdiff.cpp | 441 ++++++++++++++++++++++++-- applypatch/include/applypatch/imgdiff_image.h | 58 +++- tests/component/imgdiff_test.cpp | 339 +++++++++++++++++++- 3 files changed, 814 insertions(+), 24 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 59b600713..2eb618fbf 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -125,6 +125,7 @@ #include #include +#include #include #include #include @@ -139,16 +140,19 @@ #include #include #include +#include #include #include #include #include #include "applypatch/imgdiff_image.h" +#include "rangeset.h" using android::base::get_unaligned; -static constexpr auto BUFFER_SIZE = 0x8000; +static constexpr size_t BLOCK_SIZE = 4096; +static constexpr size_t BUFFER_SIZE = 0x8000; // If we use this function to write the offset and length (type size_t), their values should not // exceed 2^63; because the signed bit will be casted away. @@ -162,6 +166,67 @@ static inline bool Write4(int fd, int32_t value) { return android::base::WriteFully(fd, &value, sizeof(int32_t)); } +// Trim the head or tail to align with the block size. Return false if the chunk has nothing left +// after alignment. +static bool AlignHead(size_t* start, size_t* length) { + size_t residual = (*start % BLOCK_SIZE == 0) ? 0 : BLOCK_SIZE - *start % BLOCK_SIZE; + + if (*length <= residual) { + *length = 0; + return false; + } + + // Trim the data in the beginning. + *start += residual; + *length -= residual; + return true; +} + +static bool AlignTail(size_t* start, size_t* length) { + size_t residual = (*start + *length) % BLOCK_SIZE; + if (*length <= residual) { + *length = 0; + return false; + } + + // Trim the data in the end. + *length -= residual; + return true; +} + +// Remove the used blocks from the source chunk to make sure the source ranges are mutually +// exclusive after split. Return false if we fail to get the non-overlapped ranges. In such +// a case, we'll skip the entire source chunk. +static bool RemoveUsedBlocks(size_t* start, size_t* length, const SortedRangeSet& used_ranges) { + if (!used_ranges.Overlaps(*start, *length)) { + return true; + } + + // TODO find the largest non-overlap chunk. + printf("Removing block %s from %zu - %zu\n", used_ranges.ToString().c_str(), *start, + *start + *length - 1); + + // If there's no duplicate entry name, we should only overlap in the head or tail block. Try to + // trim both blocks. Skip this source chunk in case it still overlaps with the used ranges. + if (AlignHead(start, length) && !used_ranges.Overlaps(*start, *length)) { + return true; + } + if (AlignTail(start, length) && !used_ranges.Overlaps(*start, *length)) { + return true; + } + + printf("Failed to remove the overlapped block ranges; skip the source\n"); + return false; +} + +static const struct option OPTIONS[] = { + { "zip-mode", no_argument, nullptr, 'z' }, + { "bonus-file", required_argument, nullptr, 'b' }, + { "block-limit", required_argument, nullptr, 0 }, + { "debug-dir", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 }, +}; + ImageChunk::ImageChunk(int type, size_t start, const std::vector* file_content, size_t raw_data_len, std::string entry_name) : type_(type), @@ -371,6 +436,12 @@ bool PatchChunk::RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size) { return (tgt.GetType() == CHUNK_NORMAL && (target_len <= 160 || target_len < patch_size)); } +void PatchChunk::UpdateSourceOffset(const SortedRangeSet& src_range) { + if (type_ == CHUNK_DEFLATE) { + source_start_ = src_range.GetOffsetInRangeSet(source_start_); + } +} + // Header size: // header_type 4 bytes // CHUNK_NORMAL 8*3 = 24 bytes @@ -572,7 +643,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl ZipString name; ZipEntry entry; while ((ret = Next(cookie, &entry, &name)) == 0) { - if (entry.method == kCompressDeflated) { + if (entry.method == kCompressDeflated || limit_ > 0) { std::string entry_name(name.name, name.name + name.name_length); temp_entries.emplace_back(entry_name, entry); } @@ -595,6 +666,17 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl return false; } } + + // Add the end of zip file (mainly central directory) as a normal chunk. + size_t entries_end = 0; + if (!temp_entries.empty()) { + entries_end = static_cast(temp_entries.back().second.offset + + temp_entries.back().second.compressed_length); + } + CHECK_LT(entries_end, file_content_.size()); + chunks_.emplace_back(CHUNK_NORMAL, entries_end, &file_content_, + file_content_.size() - entries_end); + return true; } @@ -635,7 +717,21 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry) { size_t compressed_len = entry->compressed_length; - if (entry->method == kCompressDeflated) { + if (compressed_len == 0) return true; + + // Split the entry into several normal chunks if it's too large. + if (limit_ > 0 && compressed_len > limit_) { + int count = 0; + while (compressed_len > 0) { + size_t length = std::min(limit_, compressed_len); + std::string name = entry_name + "-" + std::to_string(count); + chunks_.emplace_back(CHUNK_NORMAL, entry->offset + limit_ * count, &file_content_, length, + name); + + count++; + compressed_len -= length; + } + } else if (entry->method == kCompressDeflated) { size_t uncompressed_len = entry->uncompressed_length; std::vector uncompressed_data(uncompressed_len); int ret = ExtractToMemory(handle, entry, uncompressed_data.data(), uncompressed_len); @@ -697,10 +793,23 @@ const ImageChunk* ZipModeImage::FindChunkByName(const std::string& name, bool fi return nullptr; } for (auto& chunk : chunks_) { - if ((chunk.GetType() == CHUNK_DEFLATE || find_normal) && chunk.GetEntryName() == name) { + if (chunk.GetType() != CHUNK_DEFLATE && !find_normal) { + continue; + } + + if (chunk.GetEntryName() == name) { return &chunk; } + + // Edge case when target chunk is split due to size limit but source chunk isn't. + if (name == (chunk.GetEntryName() + "-0") || chunk.GetEntryName() == (name + "-0")) { + return &chunk; + } + + // TODO handle the .so files with incremental version number. + // (e.g. lib/arm64-v8a/libcronet.59.0.3050.4.so) } + return nullptr; } @@ -738,24 +847,214 @@ bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* // For zips, we only need merge normal chunks for the target: deflated chunks are matched via // filename, and normal chunks are patched using the entire source file as the source. - tgt_image->MergeAdjacentNormalChunks(); - tgt_image->DumpChunks(); + if (tgt_image->limit_ == 0) { + tgt_image->MergeAdjacentNormalChunks(); + tgt_image->DumpChunks(); + } return true; } -bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image, - const std::string& patch_name) { +// For each target chunk, look for the corresponding source chunk by the zip_entry name. If +// found, add the range of this chunk in the original source file to the block aligned source +// ranges. Construct the split src & tgt image once the size of source range reaches limit. +bool ZipModeImage::SplitZipModeImageWithLimit(const ZipModeImage& tgt_image, + const ZipModeImage& src_image, + std::vector* split_tgt_images, + std::vector* split_src_images, + std::vector* split_src_ranges) { + CHECK_EQ(tgt_image.limit_, src_image.limit_); + size_t limit = tgt_image.limit_; + + src_image.DumpChunks(); + printf("Splitting %zu tgt chunks...\n", tgt_image.NumOfChunks()); + + SortedRangeSet used_src_ranges; // ranges used for previous split source images. + + // Reserve the central directory in advance for the last split image. + const auto& central_directory = src_image.cend() - 1; + CHECK_EQ(CHUNK_NORMAL, central_directory->GetType()); + used_src_ranges.Insert(central_directory->GetStartOffset(), + central_directory->DataLengthForPatch()); + + SortedRangeSet src_ranges; + std::vector split_src_chunks; + std::vector split_tgt_chunks; + for (auto tgt = tgt_image.cbegin(); tgt != tgt_image.cend(); tgt++) { + const ImageChunk* src = src_image.FindChunkByName(tgt->GetEntryName(), true); + if (src == nullptr) { + split_tgt_chunks.emplace_back(CHUNK_NORMAL, tgt->GetStartOffset(), &tgt_image.file_content_, + tgt->GetRawDataLength()); + continue; + } + + size_t src_offset = src->GetStartOffset(); + size_t src_length = src->GetRawDataLength(); + + CHECK(src_length > 0); + CHECK_LE(src_length, limit); + + // Make sure this source range hasn't been used before so that the src_range pieces don't + // overlap with each other. + if (!RemoveUsedBlocks(&src_offset, &src_length, used_src_ranges)) { + split_tgt_chunks.emplace_back(CHUNK_NORMAL, tgt->GetStartOffset(), &tgt_image.file_content_, + tgt->GetRawDataLength()); + } else if (src_ranges.blocks() * BLOCK_SIZE + src_length <= limit) { + src_ranges.Insert(src_offset, src_length); + + // Add the deflate source chunk if it hasn't been aligned. + if (src->GetType() == CHUNK_DEFLATE && src_length == src->GetRawDataLength()) { + split_src_chunks.push_back(*src); + split_tgt_chunks.push_back(*tgt); + } else { + // TODO split smarter to avoid alignment of large deflate chunks + split_tgt_chunks.emplace_back(CHUNK_NORMAL, tgt->GetStartOffset(), &tgt_image.file_content_, + tgt->GetRawDataLength()); + } + } else { + ZipModeImage::AddSplitImageFromChunkList(tgt_image, src_image, src_ranges, split_tgt_chunks, + split_src_chunks, split_tgt_images, + split_src_images); + + split_tgt_chunks.clear(); + split_src_chunks.clear(); + used_src_ranges.Insert(src_ranges); + split_src_ranges->push_back(std::move(src_ranges)); + src_ranges.Clear(); + + // We don't have enough space for the current chunk; start a new split image and handle + // this chunk there. + tgt--; + } + } + + // TODO Trim it in case the CD exceeds limit too much. + src_ranges.Insert(central_directory->GetStartOffset(), central_directory->DataLengthForPatch()); + ZipModeImage::AddSplitImageFromChunkList(tgt_image, src_image, src_ranges, split_tgt_chunks, + split_src_chunks, split_tgt_images, split_src_images); + split_src_ranges->push_back(std::move(src_ranges)); + + ValidateSplitImages(*split_tgt_images, *split_src_images, *split_src_ranges, + tgt_image.file_content_.size()); + + return true; +} + +void ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image, + const ZipModeImage& src_image, + const SortedRangeSet& split_src_ranges, + const std::vector& split_tgt_chunks, + const std::vector& split_src_chunks, + std::vector* split_tgt_images, + std::vector* split_src_images) { + CHECK(!split_tgt_chunks.empty()); + // Target chunks should occupy at least one block. + // TODO put a warning and change the type to raw if it happens in extremely rare cases. + size_t tgt_size = split_tgt_chunks.back().GetStartOffset() + + split_tgt_chunks.back().DataLengthForPatch() - + split_tgt_chunks.front().GetStartOffset(); + CHECK_GE(tgt_size, BLOCK_SIZE); + + std::vector aligned_tgt_chunks; + + // Align the target chunks in the beginning with BLOCK_SIZE. + size_t i = 0; + while (i < split_tgt_chunks.size()) { + size_t tgt_start = split_tgt_chunks[i].GetStartOffset(); + size_t tgt_length = split_tgt_chunks[i].GetRawDataLength(); + + // Current ImageChunk is long enough to align. + if (AlignHead(&tgt_start, &tgt_length)) { + aligned_tgt_chunks.emplace_back(CHUNK_NORMAL, tgt_start, &tgt_image.file_content_, + tgt_length); + break; + } + + i++; + } + CHECK_LT(i, split_tgt_chunks.size()); + aligned_tgt_chunks.insert(aligned_tgt_chunks.end(), split_tgt_chunks.begin() + i + 1, + split_tgt_chunks.end()); + CHECK(!aligned_tgt_chunks.empty()); + + // Add a normal chunk to align the contents in the end. + size_t end_offset = + aligned_tgt_chunks.back().GetStartOffset() + aligned_tgt_chunks.back().GetRawDataLength(); + if (end_offset % BLOCK_SIZE != 0 && end_offset < tgt_image.file_content_.size()) { + aligned_tgt_chunks.emplace_back(CHUNK_NORMAL, end_offset, &tgt_image.file_content_, + BLOCK_SIZE - (end_offset % BLOCK_SIZE)); + } + + ZipModeImage split_tgt_image(false); + split_tgt_image.Initialize(std::move(aligned_tgt_chunks), {}); + split_tgt_image.MergeAdjacentNormalChunks(); + + // Construct the dummy source file based on the src_ranges. + std::vector src_content; + for (const auto& r : split_src_ranges) { + size_t end = std::min(src_image.file_content_.size(), r.second * BLOCK_SIZE); + src_content.insert(src_content.end(), src_image.file_content_.begin() + r.first * BLOCK_SIZE, + src_image.file_content_.begin() + end); + } + + // We should not have an empty src in our design; otherwise we will encounter an error in + // bsdiff since src_content.data() == nullptr. + CHECK(!src_content.empty()); + + ZipModeImage split_src_image(true); + split_src_image.Initialize(split_src_chunks, std::move(src_content)); + + split_tgt_images->push_back(std::move(split_tgt_image)); + split_src_images->push_back(std::move(split_src_image)); +} + +void ZipModeImage::ValidateSplitImages(const std::vector& split_tgt_images, + const std::vector& split_src_images, + std::vector& split_src_ranges, + size_t total_tgt_size) { + CHECK_EQ(split_tgt_images.size(), split_src_images.size()); + + printf("Validating %zu images\n", split_tgt_images.size()); + + // Verify that the target image pieces is continuous and can add up to the total size. + size_t last_offset = 0; + for (const auto& tgt_image : split_tgt_images) { + CHECK(!tgt_image.chunks_.empty()); + + CHECK_EQ(last_offset, tgt_image.chunks_.front().GetStartOffset()); + CHECK(last_offset % BLOCK_SIZE == 0); + + // Check the target chunks within the split image are continuous. + for (const auto& chunk : tgt_image.chunks_) { + CHECK_EQ(last_offset, chunk.GetStartOffset()); + last_offset += chunk.GetRawDataLength(); + } + } + CHECK_EQ(total_tgt_size, last_offset); + + // Verify that the source ranges are mutually exclusive. + CHECK_EQ(split_src_images.size(), split_src_ranges.size()); + SortedRangeSet used_src_ranges; + for (size_t i = 0; i < split_src_ranges.size(); i++) { + CHECK(!used_src_ranges.Overlaps(split_src_ranges[i])) + << "src range " << split_src_ranges[i].ToString() << " overlaps " + << used_src_ranges.ToString(); + used_src_ranges.Insert(split_src_ranges[i]); + } +} + +bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image, + const ZipModeImage& src_image, + std::vector* patch_chunks) { printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks()); - std::vector patch_chunks; - patch_chunks.reserve(tgt_image.NumOfChunks()); + patch_chunks->clear(); saidx_t* bsdiff_cache = nullptr; for (size_t i = 0; i < tgt_image.NumOfChunks(); i++) { const auto& tgt_chunk = tgt_image[i]; if (PatchChunk::RawDataIsSmaller(tgt_chunk, 0)) { - patch_chunks.emplace_back(tgt_chunk); + patch_chunks->emplace_back(tgt_chunk); continue; } @@ -776,13 +1075,23 @@ bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeI tgt_chunk.GetRawDataLength()); if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) { - patch_chunks.emplace_back(tgt_chunk); + patch_chunks->emplace_back(tgt_chunk); } else { - patch_chunks.emplace_back(tgt_chunk, src_ref, std::move(patch_data)); + patch_chunks->emplace_back(tgt_chunk, src_ref, std::move(patch_data)); } } free(bsdiff_cache); + CHECK_EQ(patch_chunks->size(), tgt_image.NumOfChunks()); + return true; +} + +bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image, + const std::string& patch_name) { + std::vector patch_chunks; + + ZipModeImage::GeneratePatchesInternal(tgt_image, src_image, &patch_chunks); + CHECK_EQ(tgt_image.NumOfChunks(), patch_chunks.size()); android::base::unique_fd patch_fd( @@ -795,6 +1104,66 @@ bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeI return PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd); } +bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_images, + const std::vector& split_src_images, + const std::vector& split_src_ranges, + const std::string& patch_name, const std::string& debug_dir) { + printf("Construct patches for %zu split images...\n", split_tgt_images.size()); + + android::base::unique_fd patch_fd( + open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); + if (patch_fd == -1) { + printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno)); + return false; + } + + for (size_t i = 0; i < split_tgt_images.size(); i++) { + std::vector patch_chunks; + if (!ZipModeImage::GeneratePatchesInternal(split_tgt_images[i], split_src_images[i], + &patch_chunks)) { + printf("failed to generate split patch\n"); + return false; + } + + for (auto& p : patch_chunks) { + p.UpdateSourceOffset(split_src_ranges[i]); + } + + if (!PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd)) { + return false; + } + + // Write the split source & patch into the debug directory. + if (!debug_dir.empty()) { + std::string src_name = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i); + android::base::unique_fd fd( + open(src_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); + + if (fd == -1) { + printf("Failed to open %s\n", src_name.c_str()); + return false; + } + if (!android::base::WriteFully(fd, split_src_images[i].PseudoSource().DataForPatch(), + split_src_images[i].PseudoSource().DataLengthForPatch())) { + printf("Failed to write split source data into %s\n", src_name.c_str()); + return false; + } + + std::string patch_name = android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i); + fd.reset(open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); + + if (fd == -1) { + printf("Failed to open %s\n", patch_name.c_str()); + return false; + } + if (!PatchChunk::WritePatchDataToFd(patch_chunks, fd)) { + return false; + } + } + } + return true; +} + bool ImageModeImage::Initialize(const std::string& filename) { if (!ReadFile(filename, &file_content_)) { return false; @@ -1026,11 +1395,14 @@ bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image, int imgdiff(int argc, const char** argv) { bool zip_mode = false; std::vector bonus_data; + size_t blocks_limit = 0; + std::string debug_dir; int opt; + int option_index; optind = 1; // Reset the getopt state so that we can call it multiple times for test. - while ((opt = getopt(argc, const_cast(argv), "zb:")) != -1) { + while ((opt = getopt_long(argc, const_cast(argv), "zb:", OPTIONS, &option_index)) != -1) { switch (opt) { case 'z': zip_mode = true; @@ -1055,6 +1427,16 @@ int imgdiff(int argc, const char** argv) { } break; } + case 0: { + std::string name = OPTIONS[option_index].name; + if (name == "block-limit" && !android::base::ParseUint(optarg, &blocks_limit)) { + printf("failed to parse size blocks_limit: %s\n", optarg); + return 1; + } else if (name == "debug-dir") { + debug_dir = optarg; + } + break; + } default: printf("unexpected opt: %s\n", optarg); return 2; @@ -1062,13 +1444,20 @@ int imgdiff(int argc, const char** argv) { } if (argc - optind != 3) { - printf("usage: %s [-z] [-b ] \n", argv[0]); + printf("usage: %s [options] \n", argv[0]); + printf( + " -z , Generate patches in zip mode, src and tgt should be zip files.\n" + " -b , Bonus file in addition to src, image mode only.\n" + " --block-limit, For large zips, split the src and tgt based on the block limit;\n" + " and generate patches between each pair of pieces. Concatenate these\n" + " patches together and output them into .\n" + " --debug_dir, Debug directory to put the split srcs and patches, zip mode only.\n"); return 2; } if (zip_mode) { - ZipModeImage src_image(true); - ZipModeImage tgt_image(false); + ZipModeImage src_image(true, blocks_limit * BLOCK_SIZE); + ZipModeImage tgt_image(false, blocks_limit * BLOCK_SIZE); if (!src_image.Initialize(argv[optind])) { return 1; @@ -1080,9 +1469,25 @@ int imgdiff(int argc, const char** argv) { if (!ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)) { return 1; } + + // TODO save and output the split information so that caller can create split transfer lists + // accordingly. + // Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of // deflate chunks). - if (!ZipModeImage::GeneratePatches(tgt_image, src_image, argv[optind + 2])) { + if (blocks_limit > 0) { + std::vector split_tgt_images; + std::vector split_src_images; + std::vector split_src_ranges; + ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, + &split_src_images, &split_src_ranges); + + if (!ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges, + argv[optind + 2], debug_dir)) { + return 1; + } + + } else if (!ZipModeImage::GeneratePatches(tgt_image, src_image, argv[optind + 2])) { return 1; } } else { diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h index 221dd5ab5..9fb844b24 100644 --- a/applypatch/include/applypatch/imgdiff_image.h +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -129,6 +129,9 @@ class PatchChunk { // Return true if raw data size is smaller than the patch size. static bool RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size); + // Update the source start with the new offset within the source range. + void UpdateSourceOffset(const SortedRangeSet& src_range); + static bool WritePatchDataToFd(const std::vector& patch_chunks, int patch_fd); private: @@ -177,6 +180,14 @@ class Image { return chunks_.end(); } + std::vector::const_iterator cbegin() const { + return chunks_.cbegin(); + } + + std::vector::const_iterator cend() const { + return chunks_.cend(); + } + ImageChunk& operator[](size_t i); const ImageChunk& operator[](size_t i) const; @@ -194,10 +205,18 @@ class Image { class ZipModeImage : public Image { public: - explicit ZipModeImage(bool is_source) : Image(is_source) {} + explicit ZipModeImage(bool is_source, size_t limit = 0) : Image(is_source), limit_(limit) {} bool Initialize(const std::string& filename) override; + // Initialize a dummy ZipModeImage from an existing ImageChunk vector. For src img pieces, we + // reconstruct a new file_content based on the source ranges; but it's not needed for the tgt img + // pieces; because for each chunk both the data and their offset within the file are unchanged. + void Initialize(const std::vector& chunks, const std::vector& file_content) { + chunks_ = chunks; + file_content_ = file_content; + } + // The pesudo source chunk for bsdiff if there's no match for the given target chunk. It's in // fact the whole source file. ImageChunk PseudoSource() const; @@ -216,6 +235,21 @@ class ZipModeImage : public Image { static bool GeneratePatches(const ZipModeImage& tgt_image, const ZipModeImage& src_image, const std::string& patch_name); + // Compute the patch based on the lists of split src and tgt images. Generate patches for each + // pair of split pieces and write the data to |patch_name|. If |debug_dir| is specified, write + // each split src data and patch data into that directory. + static bool GeneratePatches(const std::vector& split_tgt_images, + const std::vector& split_src_images, + const std::vector& split_src_ranges, + const std::string& patch_name, const std::string& debug_dir); + + // Split the tgt chunks and src chunks based on the size limit. + static bool SplitZipModeImageWithLimit(const ZipModeImage& tgt_image, + const ZipModeImage& src_image, + std::vector* split_tgt_images, + std::vector* split_src_images, + std::vector* split_src_ranges); + private: // Initialize image chunks based on the zip entries. bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle); @@ -223,6 +257,28 @@ class ZipModeImage : public Image { bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry); // Return the real size of the zip file. (omit the trailing zeros that used for alignment) bool GetZipFileSize(size_t* input_file_size); + + static void ValidateSplitImages(const std::vector& split_tgt_images, + const std::vector& split_src_images, + std::vector& split_src_ranges, + size_t total_tgt_size); + // Construct the dummy split images based on the chunks info and source ranges; and move them into + // the given vectors. + static void AddSplitImageFromChunkList(const ZipModeImage& tgt_image, + const ZipModeImage& src_image, + const SortedRangeSet& split_src_ranges, + const std::vector& split_tgt_chunks, + const std::vector& split_src_chunks, + std::vector* split_tgt_images, + std::vector* split_src_images); + + // Function that actually iterates the tgt_chunks and makes patches. + static bool GeneratePatchesInternal(const ZipModeImage& tgt_image, const ZipModeImage& src_image, + std::vector* patch_chunks); + + // size limit in bytes of each chunk. Also, if the length of one zip_entry exceeds the limit, + // we'll split that entry into several smaller chunks in advance. + size_t limit_; }; class ImageModeImage : public Image { diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index bf25aebb0..3163a57cf 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -16,13 +16,17 @@ #include +#include #include +#include #include #include #include +#include #include #include +#include #include #include #include @@ -75,15 +79,20 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si if (num_deflate != nullptr) *num_deflate = deflate; } -static void verify_patched_image(const std::string& src, const std::string& patch, - const std::string& tgt) { - std::string patched; +static void GenerateTarget(const std::string& src, const std::string& patch, std::string* patched) { + patched->clear(); ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), reinterpret_cast(patch.data()), patch.size(), - [&patched](const unsigned char* data, size_t len) { - patched.append(reinterpret_cast(data), len); + [&](const unsigned char* data, size_t len) { + patched->append(reinterpret_cast(data), len); return len; })); +} + +static void verify_patched_image(const std::string& src, const std::string& patch, + const std::string& tgt) { + std::string patched; + GenerateTarget(src, patch, &patched); ASSERT_EQ(tgt, patched); } @@ -623,3 +632,323 @@ TEST(ImgpatchTest, image_mode_patch_corruption) { reinterpret_cast(patch.data()), patch.size(), [](const unsigned char* /*data*/, size_t len) { return len; })); } + +static void construct_store_entry(const std::vector>& info, + ZipWriter* writer) { + for (auto& t : info) { + // Create t(1) blocks of t(2), and write the data to t(0) + ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), 0)); + const std::string content(std::get<1>(t) * 4096, std::get<2>(t)); + ASSERT_EQ(0, writer->WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, writer->FinishEntry()); + } +} + +static void construct_deflate_entry(const std::vector>& info, + ZipWriter* writer, const std::string& data) { + for (auto& t : info) { + // t(0): entry_name; t(1): block offset; t(2) length in blocks. + ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), ZipWriter::kCompress)); + ASSERT_EQ(0, writer->WriteBytes(data.data() + std::get<1>(t) * 4096, std::get<2>(t) * 4096)); + ASSERT_EQ(0, writer->FinishEntry()); + } +} + +// Look for the generated source and patch pieces in the debug_dir and generate the target on +// each pair. Concatenate the split target and match against the orignal one. +static void GenerateAndCheckSplitTarget(const std::string& debug_dir, size_t count, + const std::string& tgt) { + std::string patched; + for (size_t i = 0; i < count; i++) { + std::string split_src_path = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i); + std::string split_patch_path = android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i); + + std::string split_src; + std::string split_patch; + ASSERT_TRUE(android::base::ReadFileToString(split_src_path, &split_src)); + ASSERT_TRUE(android::base::ReadFileToString(split_patch_path, &split_patch)); + + std::string split_tgt; + GenerateTarget(split_src, split_patch, &split_tgt); + patched += split_tgt; + } + + // Verify we can get back the original target image. + ASSERT_EQ(tgt, patched); +} + +std::vector ConstructImageChunks( + const std::vector& content, const std::vector>& info) { + std::vector chunks; + size_t start = 0; + for (const auto& t : info) { + size_t length = std::get<1>(t); + chunks.emplace_back(CHUNK_NORMAL, start, &content, length, std::get<0>(t)); + start += length; + } + + return chunks; +} + +TEST(ImgdiffTest, zip_mode_split_image_smoke) { + std::vector content; + content.reserve(4096 * 50); + uint8_t n = 0; + generate_n(back_inserter(content), 4096 * 50, [&n]() { return n++ / 4096; }); + + ZipModeImage tgt_image(false, 4096 * 10); + std::vector tgt_chunks = ConstructImageChunks(content, { { "a", 100 }, + { "b", 4096 * 2 }, + { "c", 4096 * 3 }, + { "d", 300 }, + { "e-0", 4096 * 10 }, + { "e-1", 4096 * 5 }, + { "CD", 200 } }); + tgt_image.Initialize(std::move(tgt_chunks), + std::vector(content.begin(), content.begin() + 82520)); + + tgt_image.DumpChunks(); + + ZipModeImage src_image(true, 4096 * 10); + std::vector src_chunks = ConstructImageChunks(content, { { "b", 4096 * 3 }, + { "c-0", 4096 * 10 }, + { "c-1", 4096 * 2 }, + { "a", 4096 * 5 }, + { "e-0", 4096 * 10 }, + { "e-1", 10000 }, + { "CD", 5000 } }); + src_image.Initialize(std::move(src_chunks), + std::vector(content.begin(), content.begin() + 137880)); + + std::vector split_tgt_images; + std::vector split_src_images; + std::vector split_src_ranges; + + ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, + &split_src_images, &split_src_ranges); + + // src_piece 1: a 5 blocks, b 3 blocks + // src_piece 2: c-0 10 blocks + // src_piece 3: d 0 block, e-0 10 blocks + // src_piece 4: e-1 2 blocks; CD 2 blocks + ASSERT_EQ(split_tgt_images.size(), split_src_images.size()); + ASSERT_EQ(static_cast(4), split_tgt_images.size()); + + ASSERT_EQ(static_cast(1), split_tgt_images[0].NumOfChunks()); + ASSERT_EQ(static_cast(12288), split_tgt_images[0][0].DataLengthForPatch()); + ASSERT_EQ("4,0,3,15,20", split_src_ranges[0].ToString()); + + ASSERT_EQ(static_cast(1), split_tgt_images[1].NumOfChunks()); + ASSERT_EQ(static_cast(12288), split_tgt_images[1][0].DataLengthForPatch()); + ASSERT_EQ("2,3,13", split_src_ranges[1].ToString()); + + ASSERT_EQ(static_cast(1), split_tgt_images[2].NumOfChunks()); + ASSERT_EQ(static_cast(40960), split_tgt_images[2][0].DataLengthForPatch()); + ASSERT_EQ("2,20,30", split_src_ranges[2].ToString()); + + ASSERT_EQ(static_cast(1), split_tgt_images[3].NumOfChunks()); + ASSERT_EQ(static_cast(16984), split_tgt_images[3][0].DataLengthForPatch()); + ASSERT_EQ("2,30,34", split_src_ranges[3].ToString()); +} + +TEST(ImgdiffTest, zip_mode_store_large_apk) { + // Construct src and tgt zip files with limit = 10 blocks. + // src tgt + // 12 blocks 'd' 3 blocks 'a' + // 8 blocks 'c' 3 blocks 'b' + // 3 blocks 'b' 8 blocks 'c' (exceeds limit) + // 3 blocks 'a' 12 blocks 'd' (exceeds limit) + // 3 blocks 'e' + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + construct_store_entry( + { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } }, + &tgt_writer); + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + ZipWriter src_writer(src_file_ptr); + construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } }, + &src_writer); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch. + TemporaryFile patch_file; + TemporaryDir debug_dir; + std::vector args = { + "imgdiff", "-z", "--block-limit=10", android::base::StringPrintf( + "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect 4 pieces of patch.(Rougly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e') + GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt); +} + +TEST(ImgdiffTest, zip_mode_deflate_large_apk) { + // Generate 50 blocks of random data. + std::string random_data; + random_data.reserve(4096 * 50); + generate_n(back_inserter(random_data), 4096 * 50, []() { return rand() % 256; }); + + // Construct src and tgt zip files with limit = 10 blocks. + // src tgt + // 22 blocks, "d" 4 blocks, "a" + // 5 blocks, "b" 4 blocks, "b" + // 3 blocks, "a" 7 blocks, "c" (exceeds limit) + // 8 blocks, "c" 20 blocks, "d" (exceeds limit) + // 1 block, "f" 2 blocks, "e" + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + construct_deflate_entry( + { { "a", 0, 4 }, { "b", 5, 4 }, { "c", 12, 8 }, { "d", 21, 20 }, { "e", 45, 2 }, + { "f", 48, 1 } }, &tgt_writer, random_data); + + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + ZipWriter src_writer(src_file_ptr); + + construct_deflate_entry( + { { "d", 21, 22 }, { "b", 5, 5 }, { "a", 0, 3 }, { "g", 9, 1 }, { "c", 11, 8 }, + { "f", 45, 1 } }, &src_writer, random_data); + + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + ZipModeImage src_image(true, 10 * 4096); + ZipModeImage tgt_image(false, 10 * 4096); + ASSERT_TRUE(src_image.Initialize(src_file.path)); + ASSERT_TRUE(tgt_image.Initialize(tgt_file.path)); + ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)); + + src_image.DumpChunks(); + tgt_image.DumpChunks(); + + std::vector split_tgt_images; + std::vector split_src_images; + std::vector split_src_ranges; + ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, + &split_src_images, &split_src_ranges); + + // src_piece 1: a 3 blocks, b 5 blocks + // src_piece 2: c 8 blocks + // src_piece 3: d-0 10 block + // src_piece 4: d-1 10 blocks + // src_piece 5: e 1 block, CD + ASSERT_EQ(split_tgt_images.size(), split_src_images.size()); + ASSERT_EQ(static_cast(5), split_tgt_images.size()); + + ASSERT_EQ(static_cast(2), split_src_images[0].NumOfChunks()); + ASSERT_EQ("a", split_src_images[0][0].GetEntryName()); + ASSERT_EQ("b", split_src_images[0][1].GetEntryName()); + + ASSERT_EQ(static_cast(1), split_src_images[1].NumOfChunks()); + ASSERT_EQ("c", split_src_images[1][0].GetEntryName()); + + ASSERT_EQ(static_cast(0), split_src_images[2].NumOfChunks()); + ASSERT_EQ(static_cast(0), split_src_images[3].NumOfChunks()); + ASSERT_EQ(static_cast(0), split_src_images[4].NumOfChunks()); + + // Compute patch. + TemporaryFile patch_file; + TemporaryDir debug_dir; + ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges, + patch_file.path, debug_dir.path)); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"] + GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt); +} + +TEST(ImgdiffTest, zip_mode_no_match_source) { + // Generate 20 blocks of random data. + std::string random_data; + random_data.reserve(4096 * 20); + generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); + + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer, + random_data); + + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + // We don't have a matching source entry. + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + ZipWriter src_writer(src_file_ptr); + construct_store_entry({ { "d", 1, 'd' } }, &src_writer); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch. + TemporaryFile patch_file; + TemporaryDir debug_dir; + std::vector args = { + "imgdiff", "-z", "--block-limit=10", android::base::StringPrintf( + "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect 1 pieces of patch due to no matching source entry. + GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); +} + +TEST(ImgdiffTest, zip_mode_large_enough_limit) { + // Generate 20 blocks of random data. + std::string random_data; + random_data.reserve(4096 * 20); + generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); + + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data); + + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + // Construct 10 blocks of source. + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + ZipWriter src_writer(src_file_ptr); + construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch with a limit of 20 blocks. + TemporaryFile patch_file; + TemporaryDir debug_dir; + std::vector args = { + "imgdiff", "-z", "--block-limit=20", android::base::StringPrintf( + "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect 1 pieces of patch since limit is larger than the zip file size. + GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); +} -- cgit v1.2.3 From 8f84774a68e54f08dd0b5285bd524b56966f3f52 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 6 Sep 2017 06:24:06 -0700 Subject: Import translations. DO NOT MERGE Change-Id: If7aad0299a372403f2875fe3aeb60ad07acb7b47 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- tools/recovery_l10n/res/values-pa/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml index 8564c9c36..abc771978 100644 --- a/tools/recovery_l10n/res/values-pa/strings.xml +++ b/tools/recovery_l10n/res/values-pa/strings.xml @@ -3,7 +3,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" "ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ" - "ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ" + "ਕੋਈ ਆਦੇਸ਼ ਨਹੀਂ" "ਅਸ਼ੁੱਧੀ!" "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" -- cgit v1.2.3 From e15d7a5104978cd8399501636aec0df9c1a4823c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 7 Sep 2017 13:38:51 -0700 Subject: ui: Manage menu_ with std::vector. Prior to this CL, menu_ is allocated with a fixed length of text_rows_. However, because we support scrollable menu in wear_ui, there might be more menu entries than text_rows_, which would lead to out-of-bounds array access. This CL addresses the issue by switching to std::vector. Bug: 65416558 Test: Run 'View recovery logs' on angler. Test: Set large margin height that leaves text_rows less than 21. Then run 'View recovery logs' with 21 menu entries. Change-Id: I5d4e3a0a097039e1104eda7d494c6269053dc894 --- screen_ui.cpp | 16 +++++++--------- screen_ui.h | 3 ++- wear_ui.cpp | 13 ++++++------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index 5c93b6672..b8f6ea28b 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -69,7 +69,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() text_top_(0), show_text(false), show_text_ever(false), - menu_(nullptr), + menu_headers_(nullptr), show_menu(false), menu_items(0), menu_sel(0), @@ -356,10 +356,10 @@ void ScreenRecoveryUI::draw_screen_locked() { DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); // Bold white text for the selected item. SetColor(MENU_SEL_FG); - y += DrawTextLine(x, y, menu_[i], true); + y += DrawTextLine(x, y, menu_[i].c_str(), true); SetColor(MENU); } else { - y += DrawTextLine(x, y, menu_[i], false); + y += DrawTextLine(x, y, menu_[i].c_str(), false); } } y += DrawHorizontalRule(y); @@ -508,7 +508,6 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { text_ = Alloc2d(text_rows_, text_cols_ + 1); file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); - menu_ = Alloc2d(text_rows_, text_cols_ + 1); text_col_ = text_row_ = 0; text_top_ = 1; @@ -771,12 +770,11 @@ void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* pthread_mutex_lock(&updateMutex); if (text_rows_ > 0 && text_cols_ > 0) { menu_headers_ = headers; - size_t i = 0; - for (; i < text_rows_ && items[i] != nullptr; ++i) { - strncpy(menu_[i], items[i], text_cols_ - 1); - menu_[i][text_cols_ - 1] = '\0'; + menu_.clear(); + for (size_t i = 0; i < text_rows_ && items[i] != nullptr; ++i) { + menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1))); } - menu_items = i; + menu_items = static_cast(menu_.size()); show_menu = true; menu_sel = initial_selection; update_screen_locked(); diff --git a/screen_ui.h b/screen_ui.h index 62dda7558..8231a2ba0 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -21,6 +21,7 @@ #include #include +#include #include "ui.h" @@ -127,7 +128,7 @@ class ScreenRecoveryUI : public RecoveryUI { bool show_text; bool show_text_ever; // has show_text ever been true? - char** menu_; + std::vector menu_; const char* const* menu_headers_; bool show_menu; int menu_items, menu_sel; diff --git a/wear_ui.cpp b/wear_ui.cpp index 670050a0b..edc39cfb4 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -127,11 +127,11 @@ void WearRecoveryUI::draw_screen_locked() { // white text of selected item SetColor(MENU_SEL_FG); if (menu_[i][0]) { - gr_text(gr_sys_font(), x + 4, y, menu_[i], 1); + gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 1); } SetColor(MENU); } else if (menu_[i][0]) { - gr_text(gr_sys_font(), x + 4, y, menu_[i], 0); + gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 0); } y += char_height_ + 4; } @@ -199,17 +199,16 @@ void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* it pthread_mutex_lock(&updateMutex); if (text_rows_ > 0 && text_cols_ > 0) { menu_headers_ = headers; - size_t i = 0; + menu_.clear(); // "i < text_rows_" is removed from the loop termination condition, // which is different from the one in ScreenRecoveryUI::StartMenu(). // Because WearRecoveryUI supports scrollable menu, it's fine to have // more entries than text_rows_. The menu may be truncated otherwise. // Bug: 23752519 - for (; items[i] != nullptr; i++) { - strncpy(menu_[i], items[i], text_cols_ - 1); - menu_[i][text_cols_ - 1] = '\0'; + for (size_t i = 0; items[i] != nullptr; i++) { + menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1))); } - menu_items = i; + menu_items = static_cast(menu_.size()); show_menu = true; menu_sel = initial_selection; menu_start = 0; -- cgit v1.2.3 From f05e2bcff297a5735e75e50ba42fc9c7098a579e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 5 Sep 2017 15:27:41 -0700 Subject: wear_ui: Remove Print()/ShowFile()/PutChar(). They're mostly identical to the ones in ScreenRecoveryUI, except for the (legacy) use of 'text_top_'. Because wear_ui.cpp misses the change in [1] that uses an alternate screen for viewing recovery logs. Also clean up the included headers. [1] commit c049163234003ef463bca018920622bc8269c69b ('Add an alternate screen for viewing recovery logs.'). Test: Build a wearable target recovery; `View recovery logs`. Change-Id: Ic9208c42a11c037469f5b073ef7d9b721c14d1f3 --- wear_ui.cpp | 114 +----------------------------------------------------------- wear_ui.h | 9 ----- 2 files changed, 1 insertion(+), 122 deletions(-) diff --git a/wear_ui.cpp b/wear_ui.cpp index 670050a0b..1e8e7ba05 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -16,29 +16,16 @@ #include "wear_ui.h" -#include -#include #include -#include -#include +#include // TODO: Remove after killing the call to sprintf(). #include -#include -#include -#include -#include -#include #include -#include #include -#include #include #include -#include "common.h" -#include "device.h" - WearRecoveryUI::WearRecoveryUI() : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), kMenuUnusableRows(RECOVERY_UI_MENU_UNUSABLE_ROWS) { @@ -166,34 +153,6 @@ void WearRecoveryUI::update_progress_locked() { void WearRecoveryUI::SetStage(int current, int max) { } -void WearRecoveryUI::Print(const char* fmt, ...) { - char buf[256]; - va_list ap; - va_start(ap, fmt); - vsnprintf(buf, 256, fmt, ap); - va_end(ap); - - fputs(buf, stdout); - - // This can get called before ui_init(), so be careful. - pthread_mutex_lock(&updateMutex); - if (text_rows_ > 0 && text_cols_ > 0) { - char* ptr; - for (ptr = buf; *ptr != '\0'; ++ptr) { - if (*ptr == '\n' || text_col_ >= text_cols_) { - text_[text_row_][text_col_] = '\0'; - text_col_ = 0; - text_row_ = (text_row_ + 1) % text_rows_; - if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; - } - if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; - } - text_[text_row_][text_col_] = '\0'; - update_screen_locked(); - } - pthread_mutex_unlock(&updateMutex); -} - void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* items, int initial_selection) { pthread_mutex_lock(&updateMutex); @@ -241,74 +200,3 @@ int WearRecoveryUI::SelectMenu(int sel) { pthread_mutex_unlock(&updateMutex); return sel; } - -void WearRecoveryUI::ShowFile(FILE* fp) { - std::vector offsets; - offsets.push_back(ftello(fp)); - ClearText(); - - struct stat sb; - fstat(fileno(fp), &sb); - - bool show_prompt = false; - while (true) { - if (show_prompt) { - Print("--(%d%% of %d bytes)--", - static_cast(100 * (double(ftello(fp)) / double(sb.st_size))), - static_cast(sb.st_size)); - Redraw(); - while (show_prompt) { - show_prompt = false; - int key = WaitKey(); - if (key == KEY_POWER || key == KEY_ENTER) { - return; - } else if (key == KEY_UP || key == KEY_VOLUMEUP) { - if (offsets.size() <= 1) { - show_prompt = true; - } else { - offsets.pop_back(); - fseek(fp, offsets.back(), SEEK_SET); - } - } else { - if (feof(fp)) { - return; - } - offsets.push_back(ftello(fp)); - } - } - ClearText(); - } - - int ch = getc(fp); - if (ch == EOF) { - text_row_ = text_top_ = text_rows_ - 2; - show_prompt = true; - } else { - PutChar(ch); - if (text_col_ == 0 && text_row_ >= text_rows_ - 2) { - text_top_ = text_row_; - show_prompt = true; - } - } - } -} - -void WearRecoveryUI::PutChar(char ch) { - pthread_mutex_lock(&updateMutex); - if (ch != '\n') text_[text_row_][text_col_++] = ch; - if (ch == '\n' || text_col_ >= text_cols_) { - text_col_ = 0; - ++text_row_; - } - pthread_mutex_unlock(&updateMutex); -} - -void WearRecoveryUI::ShowFile(const char* filename) { - FILE* fp = fopen_path(filename, "re"); - if (fp == nullptr) { - Print(" Unable to open %s: %s\n", filename, strerror(errno)); - return; - } - ShowFile(fp); - fclose(fp); -} diff --git a/wear_ui.h b/wear_ui.h index 92ef813f3..739b4cb1d 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -19,19 +19,12 @@ #include "screen_ui.h" -#include - class WearRecoveryUI : public ScreenRecoveryUI { public: WearRecoveryUI(); void SetStage(int current, int max) override; - // printing messages - void Print(const char* fmt, ...) override; - void ShowFile(const char* filename) override; - void ShowFile(FILE* fp) override; - // menu display void StartMenu(const char* const* headers, const char* const* items, int initial_selection) override; @@ -53,8 +46,6 @@ class WearRecoveryUI : public ScreenRecoveryUI { void draw_background_locked() override; void draw_screen_locked() override; - void PutChar(char); - int menu_start, menu_end; }; -- cgit v1.2.3 From cb5524c23a761e37e8326edb95c8d1b684af352a Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 8 Sep 2017 21:25:32 -0700 Subject: ui: Remove text_top_. After the cleanup to WearRecoveryUI, text_top_ now always equals to ((text_row_ + 1) % text_rows_). Test: Check the recovery UI and 'View recovery logs'. Change-Id: I69a7f377bbd990db2194f9d3efae257c323c06a8 --- screen_ui.cpp | 12 ++---------- screen_ui.h | 2 +- wear_ui.cpp | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index b8f6ea28b..790e956b4 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -34,8 +34,8 @@ #include #include -#include #include +#include #include "common.h" #include "device.h" @@ -66,7 +66,6 @@ ScreenRecoveryUI::ScreenRecoveryUI() text_(nullptr), text_col_(0), text_row_(0), - text_top_(0), show_text(false), show_text_ever(false), menu_headers_(nullptr), @@ -368,7 +367,7 @@ void ScreenRecoveryUI::draw_screen_locked() { // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or // we've displayed the entire text buffer. SetColor(LOG); - int row = (text_top_ + text_rows_ - 1) % text_rows_; + int row = text_row_; size_t count = 0; for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { @@ -510,7 +509,6 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); text_col_ = text_row_ = 0; - text_top_ = 1; LoadBitmap("icon_error", &error_icon); @@ -643,7 +641,6 @@ void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) text_[text_row_][text_col_] = '\0'; text_col_ = 0; text_row_ = (text_row_ + 1) % text_rows_; - if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; } if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; } @@ -673,8 +670,6 @@ void ScreenRecoveryUI::PutChar(char ch) { if (ch == '\n' || text_col_ >= text_cols_) { text_col_ = 0; ++text_row_; - - if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; } pthread_mutex_unlock(&updateMutex); } @@ -683,7 +678,6 @@ void ScreenRecoveryUI::ClearText() { pthread_mutex_lock(&updateMutex); text_col_ = 0; text_row_ = 0; - text_top_ = 1; for (size_t i = 0; i < text_rows_; ++i) { memset(text_[i], 0, text_cols_ + 1); } @@ -750,7 +744,6 @@ void ScreenRecoveryUI::ShowFile(const char* filename) { char** old_text = text_; size_t old_text_col = text_col_; size_t old_text_row = text_row_; - size_t old_text_top = text_top_; // Swap in the alternate screen and clear it. text_ = file_viewer_text_; @@ -762,7 +755,6 @@ void ScreenRecoveryUI::ShowFile(const char* filename) { text_ = old_text; text_col_ = old_text_col; text_row_ = old_text_row; - text_top_ = old_text_top; } void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* items, diff --git a/screen_ui.h b/screen_ui.h index 8231a2ba0..44ae6926b 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -123,7 +123,7 @@ class ScreenRecoveryUI : public RecoveryUI { // Log text overlay, displayed when a magic key is pressed. char** text_; - size_t text_col_, text_row_, text_top_; + size_t text_col_, text_row_; bool show_text; bool show_text_ever; // has show_text ever been true? diff --git a/wear_ui.cpp b/wear_ui.cpp index 1859b131c..e2ee48804 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -133,7 +133,7 @@ void WearRecoveryUI::draw_screen_locked() { // 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_top_ + text_rows_ - 1) % text_rows_; + 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) { -- cgit v1.2.3 From 7577965ba12ec2ea0df2e523cf335c9c8e5cfd16 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 10 Sep 2017 11:28:32 -0700 Subject: ui: Refactor the declaration orders. By grouping similar kinds together, in an order of types, constants, ctor/dtor, all other methods and data members. Also rename ScreenRecoveryUI::density_ to ScreenRecoveryUI::kDensity to align with others. Test: mmma bootable/recovery Change-Id: I1ba2d15c05ba7be8c39762f3d9dadf1fb2130de4 --- screen_ui.cpp | 4 +-- screen_ui.h | 103 +++++++++++++++++++++++++++++----------------------------- ui.h | 72 +++++++++++++++++++++++++++------------- 3 files changed, 103 insertions(+), 76 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index b8f6ea28b..c8dec4d50 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -54,7 +54,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH), kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT), kAnimationFps(RECOVERY_UI_ANIMATION_FPS), - density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), + kDensity(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), currentIcon(NONE), progressBarType(EMPTY), progressScopeStart(0), @@ -105,7 +105,7 @@ GRSurface* ScreenRecoveryUI::GetCurrentText() const { } int ScreenRecoveryUI::PixelsFromDp(int dp) const { - return dp * density_; + return dp * kDensity; } // Here's the intended layout: diff --git a/screen_ui.h b/screen_ui.h index 8231a2ba0..9bbdbf5df 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -32,6 +32,17 @@ struct GRSurface; // (shows an icon + a progress bar, text logging, menu, etc.) class ScreenRecoveryUI : public RecoveryUI { public: + enum UIElement { + HEADER, + MENU, + MENU_SEL_BG, + MENU_SEL_BG_ACTIVE, + MENU_SEL_FG, + LOG, + TEXT_FILL, + INFO + }; + ScreenRecoveryUI(); bool Init(const std::string& locale) override; @@ -67,16 +78,6 @@ class ScreenRecoveryUI : public RecoveryUI { void Redraw(); - enum UIElement { - HEADER, - MENU, - MENU_SEL_BG, - MENU_SEL_BG_ACTIVE, - MENU_SEL_FG, - LOG, - TEXT_FILL, - INFO - }; void SetColor(UIElement e) const; protected: @@ -89,7 +90,47 @@ class ScreenRecoveryUI : public RecoveryUI { const int kAnimationFps; // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. - const float density_; + const float kDensity; + + virtual bool InitTextParams(); + + virtual void draw_background_locked(); + virtual void draw_foreground_locked(); + virtual void draw_screen_locked(); + virtual void update_screen_locked(); + virtual void update_progress_locked(); + + GRSurface* GetCurrentFrame() const; + GRSurface* GetCurrentText() const; + + static void* ProgressThreadStartRoutine(void* data); + void ProgressThreadLoop(); + + virtual void ShowFile(FILE*); + virtual void PrintV(const char*, bool, va_list); + void PutChar(char); + void ClearText(); + + void LoadAnimation(); + void LoadBitmap(const char* filename, GRSurface** surface); + void LoadLocalizedBitmap(const char* filename, GRSurface** surface); + + int PixelsFromDp(int dp) const; + virtual int GetAnimationBaseline() const; + virtual int GetProgressBaseline() const; + virtual int GetTextBaseline() const; + + // Draws a highlight bar at (x, y) - (x + width, y + height). + virtual void DrawHighlightBar(int x, int y, int width, int height) const; + // Draws a horizontal rule at Y. Returns the offset it should be moving along Y-axis. + virtual int DrawHorizontalRule(int y) const; + // Draws a line of text. Returns the offset it should be moving along Y-axis. + virtual int DrawTextLine(int x, int y, const char* line, bool bold) const; + // Draws multiple text lines. Returns the offset it should be moving along Y-axis. + int DrawTextLines(int x, int y, const char* const* lines) const; + // Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines. + // Returns the offset it should be moving along Y-axis. + int DrawWrappedTextLines(int x, int y, const char* const* lines) const; Icon currentIcon; @@ -151,46 +192,6 @@ class ScreenRecoveryUI : public RecoveryUI { int char_height_; pthread_mutex_t updateMutex; - - virtual bool InitTextParams(); - - virtual void draw_background_locked(); - virtual void draw_foreground_locked(); - virtual void draw_screen_locked(); - virtual void update_screen_locked(); - virtual void update_progress_locked(); - - GRSurface* GetCurrentFrame() const; - GRSurface* GetCurrentText() const; - - static void* ProgressThreadStartRoutine(void* data); - void ProgressThreadLoop(); - - virtual void ShowFile(FILE*); - virtual void PrintV(const char*, bool, va_list); - void PutChar(char); - void ClearText(); - - void LoadAnimation(); - void LoadBitmap(const char* filename, GRSurface** surface); - void LoadLocalizedBitmap(const char* filename, GRSurface** surface); - - int PixelsFromDp(int dp) const; - virtual int GetAnimationBaseline() const; - virtual int GetProgressBaseline() const; - virtual int GetTextBaseline() const; - - // Draws a highlight bar at (x, y) - (x + width, y + height). - virtual void DrawHighlightBar(int x, int y, int width, int height) const; - // Draws a horizontal rule at Y. Returns the offset it should be moving along Y-axis. - virtual int DrawHorizontalRule(int y) const; - // Draws a line of text. Returns the offset it should be moving along Y-axis. - virtual int DrawTextLine(int x, int y, const char* line, bool bold) const; - // Draws multiple text lines. Returns the offset it should be moving along Y-axis. - int DrawTextLines(int x, int y, const char* const* lines) const; - // Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines. - // Returns the offset it should be moving along Y-axis. - int DrawWrappedTextLines(int x, int y, const char* const* lines) const; }; #endif // RECOVERY_UI_H diff --git a/ui.h b/ui.h index 3d9afece0..c6d6d6b52 100644 --- a/ui.h +++ b/ui.h @@ -26,6 +26,27 @@ // Abstract class for controlling the user interface during recovery. class RecoveryUI { public: + enum Icon { + NONE, + INSTALLING_UPDATE, + ERASING, + NO_COMMAND, + ERROR + }; + + enum ProgressType { + EMPTY, + INDETERMINATE, + DETERMINATE + }; + + enum KeyAction { + ENQUEUE, + TOGGLE, + REBOOT, + IGNORE + }; + RecoveryUI(); virtual ~RecoveryUI() {} @@ -38,12 +59,10 @@ class RecoveryUI { virtual void SetStage(int current, int max) = 0; // Sets the overall recovery state ("background image"). - enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; virtual void SetBackground(Icon icon) = 0; virtual void SetSystemUpdateText(bool security_update) = 0; // --- progress indicator --- - enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE }; virtual void SetProgressType(ProgressType determinate) = 0; // Shows a progress bar and define the scope of the next operation: @@ -94,7 +113,6 @@ class RecoveryUI { // Called on each key press, even while operations are in progress. Return value indicates whether // an immediate operation should be triggered (toggling the display, rebooting the device), or if // the key should be enqueued for use by the main thread. - enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; virtual KeyAction CheckKey(int key, bool is_long_press); // Called when a key is held down long enough to have been a long-press (but before the key is @@ -139,10 +157,37 @@ class RecoveryUI { bool touch_screen_allowed_; private: + enum class ScreensaverState { + DISABLED, + NORMAL, + DIMMED, + OFF + }; + + struct key_timer_t { + RecoveryUI* ui; + int key_code; + int count; + }; + // The sensitivity when detecting a swipe. const int kTouchLowThreshold; const int kTouchHighThreshold; + void OnKeyDetected(int key_code); + void OnTouchDetected(int dx, int dy); + int OnInputEvent(int fd, uint32_t epevents); + void ProcessKey(int key_code, int updown); + + bool IsUsbConnected(); + + static void* time_key_helper(void* cookie); + void time_key(int key_code, int count); + + void SetLocale(const std::string&); + + bool InitScreensaver(); + // Key event input queue pthread_mutex_t key_queue_mutex; pthread_cond_t key_queue_cond; @@ -172,33 +217,14 @@ class RecoveryUI { bool touch_swiping_; bool is_bootreason_recovery_ui_; - struct key_timer_t { - RecoveryUI* ui; - int key_code; - int count; - }; - pthread_t input_thread_; - void OnKeyDetected(int key_code); - void OnTouchDetected(int dx, int dy); - int OnInputEvent(int fd, uint32_t epevents); - void ProcessKey(int key_code, int updown); - - bool IsUsbConnected(); - - static void* time_key_helper(void* cookie); - void time_key(int key_code, int count); - - void SetLocale(const std::string&); - - enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF }; ScreensaverState screensaver_state_; + // The following two contain the absolute values computed from brightness_normal_ and // brightness_dimmed_ respectively. unsigned int brightness_normal_value_; unsigned int brightness_dimmed_value_; - bool InitScreensaver(); }; #endif // RECOVERY_UI_H -- cgit v1.2.3 From efb49add97cfda58c417ea4052cb6afb84c16c03 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 31 Jan 2017 23:03:10 -0800 Subject: ui: Move locale and friends into ScreenRecoveryUI class. Localized texts only make sense on devices with screens. Test: Run fake OTA on angler; check the on-screen texts. Change-Id: I3a644294c8b1f2056cfb78b2d61a598b8ddf2acf --- screen_ui.cpp | 31 +++++++++++++++++++++++++++++-- screen_ui.h | 7 +++++++ ui.cpp | 29 ++--------------------------- ui.h | 6 ------ 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index a366bb3ef..d65d656bd 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "screen_ui.h" + #include #include #include @@ -36,11 +38,10 @@ #include #include #include +#include #include "common.h" #include "device.h" -#include "minui/minui.h" -#include "screen_ui.h" #include "ui.h" // Return the current time as a double (including fractions of a second). @@ -79,6 +80,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() intro_done(false), stage(-1), max_stage(-1), + locale_(""), + rtl_locale_(false), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() const { @@ -496,6 +499,7 @@ bool ScreenRecoveryUI::InitTextParams() { bool ScreenRecoveryUI::Init(const std::string& locale) { RecoveryUI::Init(locale); + if (!InitTextParams()) { return false; } @@ -510,6 +514,9 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { text_col_ = text_row_ = 0; + // Set up the locale info. + SetLocale(locale); + LoadBitmap("icon_error", &error_icon); LoadBitmap("progress_empty", &progressBarEmpty); @@ -833,3 +840,23 @@ void ScreenRecoveryUI::KeyLongPress(int) { // will change color to indicate a successful long press. Redraw(); } + +void ScreenRecoveryUI::SetLocale(const std::string& new_locale) { + locale_ = new_locale; + rtl_locale_ = false; + + if (!new_locale.empty()) { + size_t underscore = new_locale.find('_'); + // lang has the language prefix prior to '_', or full string if '_' doesn't exist. + std::string lang = new_locale.substr(0, underscore); + + // A bit cheesy: keep an explicit list of supported RTL languages. + if (lang == "ar" || // Arabic + lang == "fa" || // Persian (Farsi) + lang == "he" || // Hebrew (new language code) + lang == "iw" || // Hebrew (old language code) + lang == "ur") { // Urdu + rtl_locale_ = true; + } + } +} diff --git a/screen_ui.h b/screen_ui.h index 0d7b9e86e..eaac2a6e8 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -191,7 +191,14 @@ class ScreenRecoveryUI : public RecoveryUI { int char_width_; int char_height_; + // The locale that's used to show the rendered texts. + std::string locale_; + bool rtl_locale_; + pthread_mutex_t updateMutex; + + private: + void SetLocale(const std::string&); }; #endif // RECOVERY_UI_H diff --git a/ui.cpp b/ui.cpp index e80d7ed04..baf6d1080 100644 --- a/ui.cpp +++ b/ui.cpp @@ -50,9 +50,7 @@ static constexpr const char* BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/br static constexpr const char* MAX_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/max_brightness"; RecoveryUI::RecoveryUI() - : locale_(""), - rtl_locale_(false), - brightness_normal_(50), + : brightness_normal_(50), brightness_dimmed_(25), touch_screen_allowed_(false), kTouchLowThreshold(RECOVERY_UI_TOUCH_LOW_THRESHOLD), @@ -132,10 +130,7 @@ bool RecoveryUI::InitScreensaver() { return true; } -bool RecoveryUI::Init(const std::string& locale) { - // Set up the locale info. - SetLocale(locale); - +bool RecoveryUI::Init(const std::string& /* locale */) { ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2), touch_screen_allowed_); @@ -574,23 +569,3 @@ void RecoveryUI::SetEnableReboot(bool enabled) { enable_reboot = enabled; pthread_mutex_unlock(&key_queue_mutex); } - -void RecoveryUI::SetLocale(const std::string& new_locale) { - this->locale_ = new_locale; - this->rtl_locale_ = false; - - if (!new_locale.empty()) { - size_t underscore = new_locale.find('_'); - // lang has the language prefix prior to '_', or full string if '_' doesn't exist. - std::string lang = new_locale.substr(0, underscore); - - // A bit cheesy: keep an explicit list of supported RTL languages. - if (lang == "ar" || // Arabic - lang == "fa" || // Persian (Farsi) - lang == "he" || // Hebrew (new language code) - lang == "iw" || // Hebrew (old language code) - lang == "ur") { // Urdu - rtl_locale_ = true; - } - } -} diff --git a/ui.h b/ui.h index c6d6d6b52..4086023af 100644 --- a/ui.h +++ b/ui.h @@ -143,10 +143,6 @@ class RecoveryUI { protected: void EnqueueKey(int key_code); - // The locale that's used to show the rendered texts. - std::string locale_; - bool rtl_locale_; - // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% of // the max_brightness). Because the absolute values may vary across devices. These two values can // be configured via subclassing. Setting brightness_normal_ to 0 to disable screensaver. @@ -184,8 +180,6 @@ class RecoveryUI { static void* time_key_helper(void* cookie); void time_key(int key_code, int count); - void SetLocale(const std::string&); - bool InitScreensaver(); // Key event input queue -- cgit v1.2.3 From 79327ac21dcc58830845c99470873c5967504cbd Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 8 Sep 2017 17:09:10 -0700 Subject: Close cmd_pipe properly after updater test finishes Otherwise the test may fail after a large number of iterations due to file open failure. Bug: 65430057 Test: run recovery_component_test on sailfish for 2000 iterations. Change-Id: I0d456284d6064467038911d63eade95740cbec2c --- tests/component/updater_test.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 6c341c111..2a0575a31 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -383,7 +383,7 @@ TEST_F(UpdaterTest, set_progress) { TemporaryFile tf; UpdaterInfo updater_info; - updater_info.cmd_pipe = fdopen(tf.fd, "w"); + updater_info.cmd_pipe = fdopen(tf.release(), "w"); expect(".52", "set_progress(\".52\")", kNoCause, &updater_info); fflush(updater_info.cmd_pipe); @@ -392,6 +392,7 @@ TEST_F(UpdaterTest, set_progress) { ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd); // recovery-updater protocol expects 2 tokens ("set_progress "). ASSERT_EQ(2U, android::base::Split(cmd, " ").size()); + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); } TEST_F(UpdaterTest, show_progress) { @@ -407,7 +408,7 @@ TEST_F(UpdaterTest, show_progress) { TemporaryFile tf; UpdaterInfo updater_info; - updater_info.cmd_pipe = fdopen(tf.fd, "w"); + updater_info.cmd_pipe = fdopen(tf.release(), "w"); expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_info); fflush(updater_info.cmd_pipe); @@ -416,12 +417,13 @@ TEST_F(UpdaterTest, show_progress) { ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd); // recovery-updater protocol expects 3 tokens ("progress "). ASSERT_EQ(3U, android::base::Split(cmd, " ").size()); + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); } TEST_F(UpdaterTest, block_image_update) { // Create a zip file with new_data and patch_data. TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); ZipWriter zip_writer(zip_file_ptr); // Add a dummy new data. @@ -485,7 +487,7 @@ TEST_F(UpdaterTest, block_image_update) { UpdaterInfo updater_info; updater_info.package_zip = handle; TemporaryFile temp_pipe; - updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe"); + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; @@ -518,7 +520,7 @@ TEST_F(UpdaterTest, block_image_update) { TEST_F(UpdaterTest, new_data_short_write) { // Create a zip file with new_data. TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); ZipWriter zip_writer(zip_file_ptr); // Add the empty new data. @@ -561,7 +563,7 @@ TEST_F(UpdaterTest, new_data_short_write) { UpdaterInfo updater_info; updater_info.package_zip = handle; TemporaryFile temp_pipe; - updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe"); + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; @@ -579,13 +581,15 @@ TEST_F(UpdaterTest, new_data_short_write) { std::string script_exact_data = "block_image_update(\"" + std::string(update_file.path) + R"(", package_extract_file("transfer_list"), "exact_new_data", "patch_data"))"; expect("t", script_exact_data.c_str(), kNoCause, &updater_info); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } TEST_F(UpdaterTest, brotli_new_data) { // Create a zip file with new_data. TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); ZipWriter zip_writer(zip_file_ptr); // Add a brotli compressed new data entry. @@ -639,7 +643,7 @@ TEST_F(UpdaterTest, brotli_new_data) { UpdaterInfo updater_info; updater_info.package_zip = handle; TemporaryFile temp_pipe; - updater_info.cmd_pipe = fopen(temp_pipe.path, "wb"); + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wb"); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; @@ -653,5 +657,7 @@ TEST_F(UpdaterTest, brotli_new_data) { std::string updated_content; ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content)); ASSERT_EQ(brotli_new_data, updated_content); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } -- cgit v1.2.3 From 4fec8e9e9a97810294c2dabe0b169198f461cafb Mon Sep 17 00:00:00 2001 From: Abhishek Arpure Date: Thu, 24 Aug 2017 15:27:16 +0530 Subject: Integer overflow observed while formatting volume While calculating volume size, get_block_device_size() returns u64 value but the returned value is assigned in ssize_t variable. This may cause integer overflow if the volume size is beyond ssize_t limit. Use int64_t instead of ssize_t in get_file_size() and explicitly check for overflow to fix the issue. Bug: 65001754 Test: mmma bootable/recovery Change-Id: I91eb30bff0bf7dcc48678efc2f414d2b79af6d0d --- roots.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/roots.cpp b/roots.cpp index fdcbfe844..26602a710 100644 --- a/roots.cpp +++ b/roots.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -178,16 +179,22 @@ static int exec_cmd(const std::vector& args) { return WEXITSTATUS(status); } -static ssize_t get_file_size(int fd, uint64_t reserve_len) { +static int64_t get_file_size(int fd, uint64_t reserve_len) { struct stat buf; int ret = fstat(fd, &buf); if (ret) return 0; - ssize_t computed_size; + int64_t computed_size; if (S_ISREG(buf.st_mode)) { computed_size = buf.st_size - reserve_len; } else if (S_ISBLK(buf.st_mode)) { - computed_size = get_block_device_size(fd) - reserve_len; + uint64_t block_device_size = get_block_device_size(fd); + if (block_device_size < reserve_len || + block_device_size > std::numeric_limits::max()) { + computed_size = 0; + } else { + computed_size = block_device_size - reserve_len; + } } else { computed_size = 0; } @@ -231,13 +238,13 @@ int format_volume(const char* volume, const char* directory) { close(fd); } - ssize_t length = 0; + int64_t length = 0; if (v->length != 0) { length = v->length; } else if (v->key_loc != nullptr && strcmp(v->key_loc, "footer") == 0) { android::base::unique_fd fd(open(v->blk_device, O_RDONLY)); if (fd == -1) { - PLOG(ERROR) << "get_file_size: failed to open " << v->blk_device; + PLOG(ERROR) << "format_volume: failed to open " << v->blk_device; return -1; } length = get_file_size(fd.get(), CRYPT_FOOTER_OFFSET); -- cgit v1.2.3 From 4d9bd3798e99bf7eaa3628a1b88da4a39b38b06d Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 13 Sep 2017 08:18:22 -0700 Subject: Import translations. DO NOT MERGE Change-Id: I8de15b09695dea1a53a368c942cb8a7dd30ed87d Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- tools/recovery_l10n/res/values-pa/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml index abc771978..27972d117 100644 --- a/tools/recovery_l10n/res/values-pa/strings.xml +++ b/tools/recovery_l10n/res/values-pa/strings.xml @@ -1,9 +1,9 @@ - "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" "ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ" "ਕੋਈ ਆਦੇਸ਼ ਨਹੀਂ" "ਅਸ਼ੁੱਧੀ!" - "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" -- cgit v1.2.3 From bd0ddcd5e8794511395bf668833acc5c2da69fb0 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 4 May 2017 13:03:18 -0700 Subject: Remove EXPAND/STRINGIFY macros. This reverts commit 8be0f39fec7f26164fd0791ff6d15bde65fc849c to reland the change that removes EXPAND/STRINGIFY macros. It's error-prone by putting anything into a string (e.g. EXPAND(RECOVERY_API_VERSION) would become "RECOVER_API_VERSION" if we forgot to pass -DRECOVERY_API_VERSION=3). The initial attempt put RECOVERY_API_VERSION into common.h, which might be included by device-specific codes but without defining that when compiling the module. This CL avoids the issue by using a constant in the header, with a static_assert in recovery.cpp that guards the consistency. Test: recovery_component_test Test: Sideload OTAs on bullhead and sailfish respectively. Change-Id: I12af3f73392a85554ba703f04970ec9d984ccbaa --- common.h | 5 +++-- install.cpp | 2 +- recovery.cpp | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/common.h b/common.h index 62fb1324b..8b336f806 100644 --- a/common.h +++ b/common.h @@ -22,8 +22,9 @@ #include -#define STRINGIFY(x) #x -#define EXPAND(x) STRINGIFY(x) +// Not using the command-line defined macro here because this header could be included by +// device-specific recovery libraries. We static assert the value consistency in recovery.cpp. +static constexpr int kRecoveryApiVersion = 3; class RecoveryUI; diff --git a/install.cpp b/install.cpp index 586dbbe2c..507161c2e 100644 --- a/install.cpp +++ b/install.cpp @@ -290,7 +290,7 @@ int update_binary_command(const std::string& package, ZipArchiveHandle zip, *cmd = { binary_path, - EXPAND(RECOVERY_API_VERSION), // defined in Android.mk + std::to_string(kRecoveryApiVersion), std::to_string(status_fd), package, }; diff --git a/recovery.cpp b/recovery.cpp index 6f62ff17c..d037b7971 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -125,6 +125,10 @@ static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15; static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe"; static constexpr const char* DEFAULT_LOCALE = "en-US"; +// 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 std::string locale; static bool has_cache = false; @@ -1498,7 +1502,7 @@ int main(int argc, char **argv) { property_list(print_property, NULL); printf("\n"); - ui->Print("Supported API: %d\n", RECOVERY_API_VERSION); + ui->Print("Supported API: %d\n", kRecoveryApiVersion); int status = INSTALL_SUCCESS; -- cgit v1.2.3 From 8ba7c45e0b6165fd0b3df72abf94bea459fcef85 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 12 Sep 2017 11:13:02 -0700 Subject: Fix the dangling pointer when setting up arguments of imgdiff Test: unit tests pass Change-Id: If884e805ccd4df73671ab3436eb90860786ff6c9 --- tests/component/imgdiff_test.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 3163a57cf..73516050b 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -779,9 +779,10 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { // Compute patch. TemporaryFile patch_file; TemporaryDir debug_dir; + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=10", android::base::StringPrintf( - "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), src_file.path, tgt_file.path, + patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); @@ -901,9 +902,10 @@ TEST(ImgdiffTest, zip_mode_no_match_source) { // Compute patch. TemporaryFile patch_file; TemporaryDir debug_dir; + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=10", android::base::StringPrintf( - "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), src_file.path, tgt_file.path, + patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); @@ -940,9 +942,10 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { // Compute patch with a limit of 20 blocks. TemporaryFile patch_file; TemporaryDir debug_dir; + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=20", android::base::StringPrintf( - "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + "imgdiff", "-z", "--block-limit=20", debug_dir_arg.c_str(), src_file.path, tgt_file.path, + patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); -- cgit v1.2.3 From 61744b95d049f78ee1483b1a8f421e95a91725fa Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Mon, 18 Sep 2017 13:36:30 -0700 Subject: recovery: reduce overall boot time Move recovery-refresh and recovery-persist to onshot exec_background. synchronous exec commands impact boot time. Test: none Bug: 65736247 Change-Id: Ic065c27b21f9bacbadee1e8c07bb15e2c41e0082 --- recovery-persist.rc | 2 +- recovery-refresh.rc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/recovery-persist.rc b/recovery-persist.rc index 6761627d5..135a3c33d 100644 --- a/recovery-persist.rc +++ b/recovery-persist.rc @@ -1,3 +1,3 @@ on post-fs-data mkdir /data/misc/recovery 0770 system log - exec - system log -- /system/bin/recovery-persist + exec_background - system log -- /system/bin/recovery-persist diff --git a/recovery-refresh.rc b/recovery-refresh.rc index 14b05cca4..9fefc819b 100644 --- a/recovery-refresh.rc +++ b/recovery-refresh.rc @@ -1,2 +1,2 @@ on post-fs - exec - system log -- /system/bin/recovery-refresh + exec_background - system log -- /system/bin/recovery-refresh -- cgit v1.2.3 From 4a47a3e41f456ad94dffd2387602688c7cabb5da Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 19 Sep 2017 23:20:46 -0700 Subject: update the recovery background image with new translation Test: recovery manual test && run graphic test under recovery Change-Id: I2bc514a07a12276f42adb1eec5e208190b4602f2 --- res-hdpi/images/erasing_text.png | Bin 50184 -> 51214 bytes res-hdpi/images/error_text.png | Bin 35856 -> 36610 bytes res-hdpi/images/installing_security_text.png | Bin 113078 -> 115058 bytes res-hdpi/images/installing_text.png | Bin 104002 -> 105531 bytes res-hdpi/images/no_command_text.png | Bin 61417 -> 62331 bytes res-mdpi/images/erasing_text.png | Bin 29660 -> 30027 bytes res-mdpi/images/error_text.png | Bin 21123 -> 21546 bytes res-mdpi/images/installing_security_text.png | Bin 69603 -> 70491 bytes res-mdpi/images/installing_text.png | Bin 61281 -> 62100 bytes res-mdpi/images/no_command_text.png | Bin 34366 -> 34772 bytes res-xhdpi/images/erasing_text.png | Bin 73097 -> 74450 bytes res-xhdpi/images/error_text.png | Bin 52180 -> 53331 bytes res-xhdpi/images/installing_security_text.png | Bin 197146 -> 201207 bytes res-xhdpi/images/installing_text.png | Bin 175660 -> 178087 bytes res-xhdpi/images/no_command_text.png | Bin 86619 -> 88037 bytes res-xxhdpi/images/erasing_text.png | Bin 121637 -> 123362 bytes res-xxhdpi/images/error_text.png | Bin 84961 -> 86841 bytes res-xxhdpi/images/installing_security_text.png | Bin 447228 -> 453013 bytes res-xxhdpi/images/installing_text.png | Bin 416000 -> 420313 bytes res-xxhdpi/images/no_command_text.png | Bin 243226 -> 247462 bytes res-xxxhdpi/images/erasing_text.png | Bin 263646 -> 267640 bytes res-xxxhdpi/images/error_text.png | Bin 178458 -> 181450 bytes res-xxxhdpi/images/installing_security_text.png | Bin 610223 -> 618212 bytes res-xxxhdpi/images/installing_text.png | Bin 567987 -> 574440 bytes res-xxxhdpi/images/no_command_text.png | Bin 331473 -> 336495 bytes 25 files changed, 0 insertions(+), 0 deletions(-) diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png index 0982544d2..34c56a966 100644 Binary files a/res-hdpi/images/erasing_text.png and b/res-hdpi/images/erasing_text.png differ diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png index 3a06f6eb1..2a96053da 100644 Binary files a/res-hdpi/images/error_text.png and b/res-hdpi/images/error_text.png differ diff --git a/res-hdpi/images/installing_security_text.png b/res-hdpi/images/installing_security_text.png index b1acd2336..97e1f11b3 100644 Binary files a/res-hdpi/images/installing_security_text.png and b/res-hdpi/images/installing_security_text.png differ diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png index f0f5d8b6c..1d591eb8b 100644 Binary files a/res-hdpi/images/installing_text.png and b/res-hdpi/images/installing_text.png differ diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png index def503678..977fcfaff 100644 Binary files a/res-hdpi/images/no_command_text.png and b/res-hdpi/images/no_command_text.png differ diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png index 82b4461ba..dcd0ea656 100644 Binary files a/res-mdpi/images/erasing_text.png and b/res-mdpi/images/erasing_text.png differ diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png index adb45131f..2152dad83 100644 Binary files a/res-mdpi/images/error_text.png and b/res-mdpi/images/error_text.png differ diff --git a/res-mdpi/images/installing_security_text.png b/res-mdpi/images/installing_security_text.png index 54e556448..d1ac4cad6 100644 Binary files a/res-mdpi/images/installing_security_text.png and b/res-mdpi/images/installing_security_text.png differ diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png index d42331820..c9b6d7185 100644 Binary files a/res-mdpi/images/installing_text.png and b/res-mdpi/images/installing_text.png differ diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png index cd77ff457..f77ad1325 100644 Binary files a/res-mdpi/images/no_command_text.png and b/res-mdpi/images/no_command_text.png differ diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png index 333edbe27..e22b27479 100644 Binary files a/res-xhdpi/images/erasing_text.png and b/res-xhdpi/images/erasing_text.png differ diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png index e26258438..e4c27e1fc 100644 Binary files a/res-xhdpi/images/error_text.png and b/res-xhdpi/images/error_text.png differ diff --git a/res-xhdpi/images/installing_security_text.png b/res-xhdpi/images/installing_security_text.png index e0f0f3ea7..7ba12b667 100644 Binary files a/res-xhdpi/images/installing_security_text.png and b/res-xhdpi/images/installing_security_text.png differ diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png index a7e67f512..567988e7f 100644 Binary files a/res-xhdpi/images/installing_text.png and b/res-xhdpi/images/installing_text.png differ diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png index 13aef7b71..a682abbef 100644 Binary files a/res-xhdpi/images/no_command_text.png and b/res-xhdpi/images/no_command_text.png differ diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png index 80e7c475e..6cc953b6d 100644 Binary files a/res-xxhdpi/images/erasing_text.png and b/res-xxhdpi/images/erasing_text.png differ diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png index 32a1965b8..0d5cea843 100644 Binary files a/res-xxhdpi/images/error_text.png and b/res-xxhdpi/images/error_text.png differ diff --git a/res-xxhdpi/images/installing_security_text.png b/res-xxhdpi/images/installing_security_text.png index c53c9ac21..5d105986a 100644 Binary files a/res-xxhdpi/images/installing_security_text.png and b/res-xxhdpi/images/installing_security_text.png differ diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png index 38b18d20d..6e94fa28b 100644 Binary files a/res-xxhdpi/images/installing_text.png and b/res-xxhdpi/images/installing_text.png differ diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png index a0666d8dc..40ab484d9 100644 Binary files a/res-xxhdpi/images/no_command_text.png and b/res-xxhdpi/images/no_command_text.png differ diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png index 4f7b37b51..cc730992b 100644 Binary files a/res-xxxhdpi/images/erasing_text.png and b/res-xxxhdpi/images/erasing_text.png differ diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png index 052bf2142..fea3cfc95 100644 Binary files a/res-xxxhdpi/images/error_text.png and b/res-xxxhdpi/images/error_text.png differ diff --git a/res-xxxhdpi/images/installing_security_text.png b/res-xxxhdpi/images/installing_security_text.png index a9e739b17..ed77d0889 100644 Binary files a/res-xxxhdpi/images/installing_security_text.png and b/res-xxxhdpi/images/installing_security_text.png differ diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png index 2d1948677..965910648 100644 Binary files a/res-xxxhdpi/images/installing_text.png and b/res-xxxhdpi/images/installing_text.png differ diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png index ee0c23865..4e6f3639d 100644 Binary files a/res-xxxhdpi/images/no_command_text.png and b/res-xxxhdpi/images/no_command_text.png differ -- cgit v1.2.3 From 82582b4562bd2ffa9ebe9d25ecdc6222b053d6ef Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 31 Aug 2017 18:05:19 -0700 Subject: Output split information for imgdiff when handling large apks Add a mandatory option in imgdiff to write the split info (i.e. patch_size, tgt_size, src_ranges) to file when handling large apks. Therefore, the caller of imgdiff can create split transfers based on the info. Bug: 63542719 Test: unit tests pass Change-Id: I853d55d1f999fd576474faa81077f7307f4d856d --- applypatch/imgdiff.cpp | 189 ++++++++++++++++++-------- applypatch/include/applypatch/imgdiff_image.h | 6 +- tests/component/imgdiff_test.cpp | 48 +++++-- 3 files changed, 174 insertions(+), 69 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 2eb618fbf..c887a854d 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -15,53 +15,44 @@ */ /* - * This program constructs binary patches for images -- such as boot.img - * and recovery.img -- that consist primarily of large chunks of gzipped - * data interspersed with uncompressed data. Doing a naive bsdiff of - * these files is not useful because small changes in the data lead to - * large changes in the compressed bitstream; bsdiff patches of gzipped - * data are typically as large as the data itself. + * This program constructs binary patches for images -- such as boot.img and recovery.img -- that + * consist primarily of large chunks of gzipped data interspersed with uncompressed data. Doing a + * naive bsdiff of these files is not useful because small changes in the data lead to large + * changes in the compressed bitstream; bsdiff patches of gzipped data are typically as large as + * the data itself. * - * To patch these usefully, we break the source and target images up into - * chunks of two types: "normal" and "gzip". Normal chunks are simply - * patched using a plain bsdiff. Gzip chunks are first expanded, then a - * bsdiff is applied to the uncompressed data, then the patched data is - * gzipped using the same encoder parameters. Patched chunks are - * concatenated together to create the output file; the output image - * should be *exactly* the same series of bytes as the target image used - * originally to generate the patch. + * To patch these usefully, we break the source and target images up into chunks of two types: + * "normal" and "gzip". Normal chunks are simply patched using a plain bsdiff. Gzip chunks are + * first expanded, then a bsdiff is applied to the uncompressed data, then the patched data is + * gzipped using the same encoder parameters. Patched chunks are concatenated together to create + * the output file; the output image should be *exactly* the same series of bytes as the target + * image used originally to generate the patch. * - * To work well with this tool, the gzipped sections of the target - * image must have been generated using the same deflate encoder that - * is available in applypatch, namely, the one in the zlib library. - * In practice this means that images should be compressed using the - * "minigzip" tool included in the zlib distribution, not the GNU gzip - * program. + * To work well with this tool, the gzipped sections of the target image must have been generated + * using the same deflate encoder that is available in applypatch, namely, the one in the zlib + * library. In practice this means that images should be compressed using the "minigzip" tool + * included in the zlib distribution, not the GNU gzip program. * - * An "imgdiff" patch consists of a header describing the chunk structure - * of the file and any encoding parameters needed for the gzipped - * chunks, followed by N bsdiff patches, one per chunk. + * An "imgdiff" patch consists of a header describing the chunk structure of the file and any + * encoding parameters needed for the gzipped chunks, followed by N bsdiff patches, one per chunk. * - * For a diff to be generated, the source and target images must have the - * same "chunk" structure: that is, the same number of gzipped and normal - * chunks in the same order. Android boot and recovery images currently - * consist of five chunks: a small normal header, a gzipped kernel, a - * small normal section, a gzipped ramdisk, and finally a small normal - * footer. + * For a diff to be generated, the source and target must be in well-formed zip archive format; + * or they are image files with the same "chunk" structure: that is, the same number of gzipped and + * normal chunks in the same order. Android boot and recovery images currently consist of five + * chunks: a small normal header, a gzipped kernel, a small normal section, a gzipped ramdisk, and + * finally a small normal footer. * - * Caveats: we locate gzipped sections within the source and target - * images by searching for the byte sequence 1f8b0800: 1f8b is the gzip - * magic number; 08 specifies the "deflate" encoding [the only encoding - * supported by the gzip standard]; and 00 is the flags byte. We do not - * currently support any extra header fields (which would be indicated by - * a nonzero flags byte). We also don't handle the case when that byte - * sequence appears spuriously in the file. (Note that it would have to - * occur spuriously within a normal chunk to be a problem.) + * Caveats: we locate gzipped sections within the source and target images by searching for the + * byte sequence 1f8b0800: 1f8b is the gzip magic number; 08 specifies the "deflate" encoding + * [the only encoding supported by the gzip standard]; and 00 is the flags byte. We do not + * currently support any extra header fields (which would be indicated by a nonzero flags byte). + * We also don't handle the case when that byte sequence appears spuriously in the file. (Note + * that it would have to occur spuriously within a normal chunk to be a problem.) * * * The imgdiff patch header looks like this: * - * "IMGDIFF1" (8) [magic number and version] + * "IMGDIFF2" (8) [magic number and version] * chunk count (4) * for each chunk: * chunk type (4) [CHUNK_{NORMAL, GZIP, DEFLATE, RAW}] @@ -98,27 +89,55 @@ * target len (4) * data (target len) * - * All integers are little-endian. "source start" and "source len" - * specify the section of the input image that comprises this chunk, - * including the gzip header and footer for gzip chunks. "source - * expanded len" is the size of the uncompressed source data. "target - * expected len" is the size of the uncompressed data after applying - * the bsdiff patch. The next five parameters specify the zlib - * parameters to be used when compressing the patched data, and the - * next three specify the header and footer to be wrapped around the - * compressed data to create the output chunk (so that header contents - * like the timestamp are recreated exactly). + * All integers are little-endian. "source start" and "source len" specify the section of the + * input image that comprises this chunk, including the gzip header and footer for gzip chunks. + * "source expanded len" is the size of the uncompressed source data. "target expected len" is the + * size of the uncompressed data after applying the bsdiff patch. The next five parameters + * specify the zlib parameters to be used when compressing the patched data, and the next three + * specify the header and footer to be wrapped around the compressed data to create the output + * chunk (so that header contents like the timestamp are recreated exactly). * - * After the header there are 'chunk count' bsdiff patches; the offset - * of each from the beginning of the file is specified in the header. + * After the header there are 'chunk count' bsdiff patches; the offset of each from the beginning + * of the file is specified in the header. * - * This tool can take an optional file of "bonus data". This is an - * extra file of data that is appended to chunk #1 after it is - * compressed (it must be a CHUNK_DEFLATE chunk). The same file must - * be available (and passed to applypatch with -b) when applying the - * patch. This is used to reduce the size of recovery-from-boot - * patches by combining the boot image with recovery ramdisk + * This tool can take an optional file of "bonus data". This is an extra file of data that is + * appended to chunk #1 after it is compressed (it must be a CHUNK_DEFLATE chunk). The same file + * must be available (and passed to applypatch with -b) when applying the patch. This is used to + * reduce the size of recovery-from-boot patches by combining the boot image with recovery ramdisk * information that is stored on the system partition. + * + * When generating the patch between two zip files, this tool has an option "--block-limit" to + * split the large source/target files into several pair of pieces, with each piece has at most + * *limit* blocks. When this option is used, we also need to output the split info into the file + * path specified by "--split-info". + * + * Format of split info file: + * 2 [version of imgdiff] + * n [count of split pieces] + * , , [size and ranges for split piece#1] + * ... + * , , [size and ranges for split piece#n] + * + * To split a pair of large zip files, we walk through the chunks in target zip and search by its + * entry_name in the source zip. If the entry_name is non-empty and a matching entry in source + * is found, we'll add the source entry to the current split source image; otherwise we'll skip + * this chunk and later do bsdiff between all the skipped trunks and the whole split source image. + * We move on to the next pair of pieces if the size of the split source image reaches the block + * limit. + * + * After the split, the target pieces are continuous and block aligned, while the source pieces + * are mutually exclusive. Some of the source blocks may not be used if there's no matching + * entry_name in the target; as a result, they won't be included in any of these split source + * images. Then we will generate patches accordingly between each split image pairs; in particular, + * the unmatched trunks in the split target will diff against the entire split source image. + * + * For example: + * Input: [src_image, tgt_image] + * Split: [src-0, tgt-0; src-1, tgt-1, src-2, tgt-2] + * Diff: [ patch-0; patch-1; patch-2] + * + * Patch: [(src-0, patch-0) = tgt-0; (src-1, patch-1) = tgt-1; (src-2, patch-2) = tgt-2] + * Concatenate: [tgt-0 + tgt-1 + tgt-2 = tgt_image] */ #include "applypatch/imgdiff.h" @@ -151,6 +170,11 @@ using android::base::get_unaligned; +static constexpr size_t VERSION = 2; + +// We assume the header "IMGDIFF#" is 8 bytes. +static_assert(VERSION <= 9, "VERSION occupies more than one byte."); + static constexpr size_t BLOCK_SIZE = 4096; static constexpr size_t BUFFER_SIZE = 0x8000; @@ -224,6 +248,7 @@ static const struct option OPTIONS[] = { { "bonus-file", required_argument, nullptr, 'b' }, { "block-limit", required_argument, nullptr, 0 }, { "debug-dir", required_argument, nullptr, 0 }, + { "split-info", required_argument, nullptr, 0 }, { nullptr, 0, nullptr, 0 }, }; @@ -497,6 +522,13 @@ size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset) const { } } +size_t PatchChunk::PatchSize() const { + if (type_ == CHUNK_RAW) { + return GetHeaderSize(); + } + return GetHeaderSize() + data_.size(); +} + // Write the contents of |patch_chunks| to |patch_fd|. bool PatchChunk::WritePatchDataToFd(const std::vector& patch_chunks, int patch_fd) { // Figure out how big the imgdiff file header is going to be, so that we can correctly compute @@ -509,8 +541,8 @@ bool PatchChunk::WritePatchDataToFd(const std::vector& patch_chunks, size_t offset = total_header_size; // Write out the headers. - if (!android::base::WriteStringToFd("IMGDIFF2", patch_fd)) { - printf("failed to write \"IMGDIFF2\": %s\n", strerror(errno)); + if (!android::base::WriteStringToFd("IMGDIFF" + std::to_string(VERSION), patch_fd)) { + printf("failed to write \"IMGDIFF%zu\": %s\n", VERSION, strerror(errno)); return false; } @@ -1107,7 +1139,9 @@ bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeI bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_images, const std::vector& split_src_images, const std::vector& split_src_ranges, - const std::string& patch_name, const std::string& debug_dir) { + const std::string& patch_name, + const std::string& split_info_file, + const std::string& debug_dir) { printf("Construct patches for %zu split images...\n", split_tgt_images.size()); android::base::unique_fd patch_fd( @@ -1117,6 +1151,7 @@ bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_im return false; } + std::vector split_info_list; for (size_t i = 0; i < split_tgt_images.size(); i++) { std::vector patch_chunks; if (!ZipModeImage::GeneratePatchesInternal(split_tgt_images[i], split_src_images[i], @@ -1125,14 +1160,23 @@ bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_im return false; } + size_t total_patch_size = 12; for (auto& p : patch_chunks) { p.UpdateSourceOffset(split_src_ranges[i]); + total_patch_size += p.PatchSize(); } if (!PatchChunk::WritePatchDataToFd(patch_chunks, patch_fd)) { return false; } + size_t split_tgt_size = split_tgt_images[i].chunks_.back().GetStartOffset() + + split_tgt_images[i].chunks_.back().GetRawDataLength() - + split_tgt_images[i].chunks_.front().GetStartOffset(); + std::string split_info = android::base::StringPrintf( + "%zu %zu %s", total_patch_size, split_tgt_size, split_src_ranges[i].ToString().c_str()); + split_info_list.push_back(split_info); + // Write the split source & patch into the debug directory. if (!debug_dir.empty()) { std::string src_name = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i); @@ -1161,6 +1205,21 @@ bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_im } } } + + // Store the split in the following format: + // Line 0: imgdiff version# + // Line 1: number of pieces + // Line 2: patch_size_1 tgt_size_1 src_range_1 + // ... + // Line n+1: patch_size_n tgt_size_n src_range_n + std::string split_info_string = android::base::StringPrintf( + "%zu\n%zu\n", VERSION, split_info_list.size()) + android::base::Join(split_info_list, '\n'); + if (!android::base::WriteStringToFile(split_info_string, split_info_file)) { + printf("failed to write split info to \"%s\": %s\n", split_info_file.c_str(), + strerror(errno)); + return false; + } + return true; } @@ -1396,6 +1455,7 @@ int imgdiff(int argc, const char** argv) { bool zip_mode = false; std::vector bonus_data; size_t blocks_limit = 0; + std::string split_info_file; std::string debug_dir; int opt; @@ -1432,6 +1492,8 @@ int imgdiff(int argc, const char** argv) { if (name == "block-limit" && !android::base::ParseUint(optarg, &blocks_limit)) { printf("failed to parse size blocks_limit: %s\n", optarg); return 1; + } else if (name == "split-info") { + split_info_file = optarg; } else if (name == "debug-dir") { debug_dir = optarg; } @@ -1451,6 +1513,8 @@ int imgdiff(int argc, const char** argv) { " --block-limit, For large zips, split the src and tgt based on the block limit;\n" " and generate patches between each pair of pieces. Concatenate these\n" " patches together and output them into .\n" + " --split-info, Output the split information (patch_size, tgt_size, src_ranges);\n" + " zip mode with block-limit only.\n" " --debug_dir, Debug directory to put the split srcs and patches, zip mode only.\n"); return 2; } @@ -1476,6 +1540,11 @@ int imgdiff(int argc, const char** argv) { // Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of // deflate chunks). if (blocks_limit > 0) { + if (split_info_file.empty()) { + printf("split-info path cannot be empty when generating patches with a block-limit.\n"); + return 1; + } + std::vector split_tgt_images; std::vector split_src_images; std::vector split_src_ranges; @@ -1483,7 +1552,7 @@ int imgdiff(int argc, const char** argv) { &split_src_images, &split_src_ranges); if (!ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges, - argv[optind + 2], debug_dir)) { + argv[optind + 2], split_info_file, debug_dir)) { return 1; } diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h index 9fb844b24..491043dc1 100644 --- a/applypatch/include/applypatch/imgdiff_image.h +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -132,6 +132,9 @@ class PatchChunk { // Update the source start with the new offset within the source range. void UpdateSourceOffset(const SortedRangeSet& src_range); + // Return the total size (header + data) of the patch. + size_t PatchSize() const; + static bool WritePatchDataToFd(const std::vector& patch_chunks, int patch_fd); private: @@ -241,7 +244,8 @@ class ZipModeImage : public Image { static bool GeneratePatches(const std::vector& split_tgt_images, const std::vector& split_src_images, const std::vector& split_src_ranges, - const std::string& patch_name, const std::string& debug_dir); + const std::string& patch_name, const std::string& split_info_file, + const std::string& debug_dir); // Split the tgt chunks and src chunks based on the size limit. static bool SplitZipModeImageWithLimit(const ZipModeImage& tgt_image, diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 73516050b..161d58d45 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -778,11 +778,13 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { // Compute patch. TemporaryFile patch_file; + TemporaryFile split_info_file; TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), src_file.path, tgt_file.path, - patch_file.path, + "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); @@ -864,14 +866,40 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { // Compute patch. TemporaryFile patch_file; + TemporaryFile split_info_file; TemporaryDir debug_dir; ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges, - patch_file.path, debug_dir.path)); + patch_file.path, split_info_file.path, debug_dir.path)); + + // Verify the content of split info. + // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"] + std::string split_info_string; + android::base::ReadFileToString(split_info_file.path, &split_info_string); + std::vector info_list = + android::base::Split(android::base::Trim(split_info_string), "\n"); + + ASSERT_EQ(static_cast(7), info_list.size()); + ASSERT_EQ("2", android::base::Trim(info_list[0])); + ASSERT_EQ("5", android::base::Trim(info_list[1])); + + std::vector patch_size; + for (size_t i = 0; i < 5; i++) { + struct stat st = {}; + std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i); + ASSERT_EQ(0, stat(path.c_str(), &st)); + patch_size.push_back(st.st_size); + } + + ASSERT_EQ(std::to_string(patch_size[0]) + " 36864 2,22,31", android::base::Trim(info_list[2])); + ASSERT_EQ(std::to_string(patch_size[1]) + " 32768 2,31,40", android::base::Trim(info_list[3])); + ASSERT_EQ(std::to_string(patch_size[2]) + " 40960 2,0,11", android::base::Trim(info_list[4])); + ASSERT_EQ(std::to_string(patch_size[3]) + " 40960 2,11,21", android::base::Trim(info_list[5])); + ASSERT_EQ(std::to_string(patch_size[4]) + " 8833 4,21,22,40,41", + android::base::Trim(info_list[6])); std::string tgt; ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"] GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt); } @@ -901,11 +929,13 @@ TEST(ImgdiffTest, zip_mode_no_match_source) { // Compute patch. TemporaryFile patch_file; + TemporaryFile split_info_file; TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), src_file.path, tgt_file.path, - patch_file.path, + "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), split_info_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); @@ -941,11 +971,13 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { // Compute patch with a limit of 20 blocks. TemporaryFile patch_file; + TemporaryFile split_info_file; TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=20", debug_dir_arg.c_str(), src_file.path, tgt_file.path, - patch_file.path, + "imgdiff", "-z", "--block-limit=20", split_info_arg.c_str(), debug_dir_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); -- cgit v1.2.3 From a1f6980af785f241d530ae99c8ce8d566e821e3d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 26 Sep 2017 14:53:00 -0700 Subject: clang-format: Remove the override of PenaltyExcessCharacter. So it falls back (from 32) to the default value in Google style: PenaltyExcessCharacter: 1000000 This way it no longer allows lines exceeding the 100-char limit. const char* x[] = { "long enough so that the line has 101 chars", nullptr }; would become const char* x[] = { "long enough so that the line has 101 chars", nullptr }; Test: clang-format the line above. Change-Id: If578dbdae55d6d81f5e47889890e2afa12b4199a --- .clang-format | 1 - 1 file changed, 1 deletion(-) diff --git a/.clang-format b/.clang-format index 532278864..0e0f4d143 100644 --- a/.clang-format +++ b/.clang-format @@ -10,6 +10,5 @@ IndentWidth: 2 PointerAlignment: Left TabWidth: 2 UseTab: Never -PenaltyExcessCharacter: 32 Cpp11BracedListStyle: false -- cgit v1.2.3 From 29d5575fa877770f6387420294d9dc184a84a115 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 20 Sep 2017 17:53:46 -0700 Subject: Add a new option in recovery menu to test the background texts Add a new option "Run locale test" to check the background text images (i.e. texts for "erasing", "error", "no_command" and "installing" with different locales.) Use volume up/down button to cycle through all the locales embedded in the png file, and power button to go back to recovery main menu. Test: Run locale test with bullhead. Change-Id: Ib16e119f372110cdb5e611ef497b0f9b9b418f51 --- device.cpp | 44 +++++++++++++------------- device.h | 1 + minui/include/minui/minui.h | 4 +++ minui/resources.cpp | 35 +++++++++++++++++++++ recovery.cpp | 5 +++ screen_ui.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++ screen_ui.h | 8 +++++ 7 files changed, 153 insertions(+), 21 deletions(-) diff --git a/device.cpp b/device.cpp index 61501869e..f881daff6 100644 --- a/device.cpp +++ b/device.cpp @@ -17,34 +17,36 @@ #include "device.h" static const char* MENU_ITEMS[] = { - "Reboot system now", - "Reboot to bootloader", - "Apply update from ADB", - "Apply update from SD card", - "Wipe data/factory reset", + "Reboot system now", + "Reboot to bootloader", + "Apply update from ADB", + "Apply update from SD card", + "Wipe data/factory reset", #ifndef AB_OTA_UPDATER - "Wipe cache partition", + "Wipe cache partition", #endif // !AB_OTA_UPDATER - "Mount /system", - "View recovery logs", - "Run graphics test", - "Power off", - NULL, + "Mount /system", + "View recovery logs", + "Run graphics test", + "Run locale test", + "Power off", + nullptr, }; static const Device::BuiltinAction MENU_ACTIONS[] = { - Device::REBOOT, - Device::REBOOT_BOOTLOADER, - Device::APPLY_ADB_SIDELOAD, - Device::APPLY_SDCARD, - Device::WIPE_DATA, + Device::REBOOT, + Device::REBOOT_BOOTLOADER, + Device::APPLY_ADB_SIDELOAD, + Device::APPLY_SDCARD, + Device::WIPE_DATA, #ifndef AB_OTA_UPDATER - Device::WIPE_CACHE, + Device::WIPE_CACHE, #endif // !AB_OTA_UPDATER - Device::MOUNT_SYSTEM, - Device::VIEW_RECOVERY_LOGS, - Device::RUN_GRAPHICS_TEST, - Device::SHUTDOWN, + Device::MOUNT_SYSTEM, + Device::VIEW_RECOVERY_LOGS, + Device::RUN_GRAPHICS_TEST, + Device::RUN_LOCALE_TEST, + Device::SHUTDOWN, }; static_assert(sizeof(MENU_ITEMS) / sizeof(MENU_ITEMS[0]) == diff --git a/device.h b/device.h index 639e2bf57..74745b36c 100644 --- a/device.h +++ b/device.h @@ -66,6 +66,7 @@ class Device { VIEW_RECOVERY_LOGS = 9, MOUNT_SYSTEM = 10, RUN_GRAPHICS_TEST = 11, + RUN_LOCALE_TEST = 12, }; // Return the list of menu items (an array of strings, NULL-terminated). The menu_position passed diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index 017ddde75..27e603136 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -21,6 +21,7 @@ #include #include +#include // // Graphics. @@ -129,6 +130,9 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface); int res_create_localized_alpha_surface(const char* name, const char* locale, GRSurface** pSurface); +// Return a list of locale strings embedded in |png_name|. Return a empty list in case of failure. +std::vector get_locales_in_png(const std::string& png_name); + // Free a surface allocated by any of the res_create_*_surface() // functions. void res_free_surface(GRSurface* surface); diff --git a/minui/resources.cpp b/minui/resources.cpp index 8f8d36d27..756f29d21 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -396,6 +396,41 @@ bool matches_locale(const std::string& prefix, const std::string& locale) { return std::regex_match(locale, loc_regex); } +std::vector get_locales_in_png(const std::string& png_name) { + png_structp png_ptr = nullptr; + png_infop info_ptr = nullptr; + png_uint_32 width, height; + png_byte channels; + + int status = open_png(png_name.c_str(), &png_ptr, &info_ptr, &width, &height, &channels); + if (status < 0) { + printf("Failed to open %s\n", png_name.c_str()); + return {}; + } + if (channels != 1) { + printf("Expect input png to have 1 data channel, this file has %d\n", channels); + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + return {}; + } + + std::vector result; + std::vector row(width); + for (png_uint_32 y = 0; y < height; ++y) { + png_read_row(png_ptr, row.data(), nullptr); + int h = (row[3] << 8) | row[2]; + std::string loc(reinterpret_cast(&row[5])); + if (!loc.empty()) { + result.push_back(loc); + } + for (int i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row.data(), NULL); + } + } + + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + return result; +} + int res_create_localized_alpha_surface(const char* name, const char* locale, GRSurface** pSurface) { diff --git a/recovery.cpp b/recovery.cpp index d037b7971..076b4492e 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1191,6 +1191,11 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { run_graphics_test(); break; + case Device::RUN_LOCALE_TEST: { + ScreenRecoveryUI* screen_ui = static_cast(ui); + screen_ui->CheckBackgroundTextImages(locale); + break; + } case Device::MOUNT_SYSTEM: // For a system image built with the root directory (i.e. system_root_image == "true"), we // mount it to /system_root, and symlink /system to /system_root/system to make adb shell diff --git a/screen_ui.cpp b/screen_ui.cpp index d65d656bd..bc5c5c31f 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -31,7 +31,9 @@ #include #include +#include #include +#include #include #include @@ -258,6 +260,81 @@ void ScreenRecoveryUI::SetColor(UIElement e) const { } } +void ScreenRecoveryUI::SelectAndShowBackgroundText(const std::vector& locales_entries, + size_t sel) { + SetLocale(locales_entries[sel]); + std::vector text_name = { "erasing_text", "error_text", "installing_text", + "installing_security_text", "no_command_text" }; + std::unordered_map> surfaces; + for (const auto& name : text_name) { + GRSurface* text_image = nullptr; + LoadLocalizedBitmap(name.c_str(), &text_image); + if (!text_image) { + Print("Failed to load %s\n", name.c_str()); + return; + } + surfaces.emplace(name, std::unique_ptr(text_image, &free)); + } + + pthread_mutex_lock(&updateMutex); + gr_color(0, 0, 0, 255); + gr_clear(); + + int text_y = kMarginHeight; + int text_x = kMarginWidth; + int line_spacing = gr_sys_font()->char_height; // Put some extra space between images. + // Write the header and descriptive texts. + SetColor(INFO); + std::string header = "Show background text image"; + text_y += DrawTextLine(text_x, text_y, header.c_str(), true); + std::string locale_selection = android::base::StringPrintf( + "Current locale: %s, %zu/%zu", locales_entries[sel].c_str(), sel, locales_entries.size()); + const char* instruction[] = { locale_selection.c_str(), + "Use volume up/down to switch locales and power to exit.", + nullptr }; + text_y += DrawWrappedTextLines(text_x, text_y, instruction); + + // Iterate through the text images and display them in order for the current locale. + for (const auto& p : surfaces) { + text_y += line_spacing; + SetColor(LOG); + text_y += DrawTextLine(text_x, text_y, p.first.c_str(), false); + gr_color(255, 255, 255, 255); + gr_texticon(text_x, text_y, p.second.get()); + text_y += gr_get_height(p.second.get()); + } + // Update the whole screen. + gr_flip(); + pthread_mutex_unlock(&updateMutex); +} + +void ScreenRecoveryUI::CheckBackgroundTextImages(const std::string& saved_locale) { + // Load a list of locales embedded in one of the resource files. + std::vector locales_entries = get_locales_in_png("installing_text"); + if (locales_entries.empty()) { + Print("Failed to load locales from the resource files\n"); + return; + } + size_t selected = 0; + SelectAndShowBackgroundText(locales_entries, selected); + + FlushKeys(); + while (true) { + int key = WaitKey(); + if (key == KEY_POWER || key == KEY_ENTER) { + break; + } else if (key == KEY_UP || key == KEY_VOLUMEUP) { + selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1; + SelectAndShowBackgroundText(locales_entries, selected); + } else if (key == KEY_DOWN || key == KEY_VOLUMEDOWN) { + selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1; + SelectAndShowBackgroundText(locales_entries, selected); + } + } + + SetLocale(saved_locale); +} + int ScreenRecoveryUI::DrawHorizontalRule(int y) const { gr_fill(0, y + 4, gr_fb_width(), y + 6); return 8; diff --git a/screen_ui.h b/screen_ui.h index eaac2a6e8..3a28a09de 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -80,6 +80,10 @@ class ScreenRecoveryUI : public RecoveryUI { void SetColor(UIElement e) const; + // Check the background text image. Use volume up/down button to cycle through the locales + // embedded in the png file, and power button to go back to recovery main menu. + void CheckBackgroundTextImages(const std::string& saved_locale); + protected: // The margin that we don't want to use for showing texts (e.g. round screen, or screen with // rounded corners). @@ -199,6 +203,10 @@ class ScreenRecoveryUI : public RecoveryUI { private: void SetLocale(const std::string&); + + // Display the background texts for "erasing", "error", "no_command" and "installing" for the + // selected locale. + void SelectAndShowBackgroundText(const std::vector& locales_entries, size_t sel); }; #endif // RECOVERY_UI_H -- cgit v1.2.3 From 2dfc1a38982c4052bb32bc7fc06edeadf3908fb9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 27 Sep 2017 13:12:11 -0700 Subject: roots: volume_for_path() parses and tries prefixes. Commit cc323958f99e40fea06c511656c69c0b2e2d47f7 in system/core has changed fs_mgr_get_entry_for_mount_point() to do an exact match only, which breaks the behavior in volume_for_path(). This CL changes the volume_for_path() implementation to parse and pass prefixes locally. For a given path like "/cache/recovery/last_log", it will in turn attempt the prefixes of "/cache/recovery/last_log", "/cache/recovery", "/cache", "/" and return the first hit. Bug: 63912287 Test: Build and boot into recovery image on bullhead. 'View recovery logs' works. Change-Id: Ic8635b0939649dd5cc9ca501ebc3a2d1fbf5849d --- roots.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/roots.cpp b/roots.cpp index fdcbfe844..835a1dda0 100644 --- a/roots.cpp +++ b/roots.cpp @@ -68,8 +68,27 @@ void load_volume_table() { printf("\n"); } +// Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match +// only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log", +// "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the +// first match or nullptr. Volume* volume_for_path(const char* path) { - return fs_mgr_get_entry_for_mount_point(fstab, path); + if (path == nullptr || path[0] == '\0') return nullptr; + std::string str(path); + while (true) { + Volume* result = fs_mgr_get_entry_for_mount_point(fstab, str.c_str()); + if (result != nullptr || str == "/") { + return result; + } + size_t slash = str.find_last_of('/'); + if (slash == std::string::npos) return nullptr; + if (slash == 0) { + str = "/"; + } else { + str = str.substr(0, slash); + } + } + return nullptr; } // Mount the volume specified by path at the given mount_point. -- cgit v1.2.3 From 846012fc444e6076dabf874ed8cbdab358c2e0fb Mon Sep 17 00:00:00 2001 From: Luke Song Date: Wed, 13 Sep 2017 15:56:16 -0700 Subject: graphics: add rotation logic Bug: 65556996 Bug: 63541890 Test: Tried 4 rotations, viewed logs and graphics test Change-Id: I2a6c18c28df03f0461663f63bf16db32c45211ec --- minui/Android.mk | 6 + minui/graphics.cpp | 459 ++++++++++++++++++++++++-------------------- minui/include/minui/minui.h | 36 ++-- 3 files changed, 280 insertions(+), 221 deletions(-) diff --git a/minui/Android.mk b/minui/Android.mk index 924698495..9a217a48f 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -58,6 +58,12 @@ else LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0 endif +ifneq ($(TARGET_RECOVERY_DEFAULT_ROTATION),) + LOCAL_CFLAGS += -DDEFAULT_ROTATION=$(TARGET_RECOVERY_DEFAULT_ROTATION) +else + LOCAL_CFLAGS += -DDEFAULT_ROTATION=ROTATION_NONE +endif + include $(BUILD_STATIC_LIBRARY) # Used by OEMs for factory test images. diff --git a/minui/graphics.cpp b/minui/graphics.cpp index 3bfce11d8..56f471bce 100644 --- a/minui/graphics.cpp +++ b/minui/graphics.cpp @@ -16,6 +16,7 @@ #include "graphics.h" +#include #include #include #include @@ -35,281 +36,311 @@ static int overscan_percent = OVERSCAN_PERCENT; static int overscan_offset_x = 0; static int overscan_offset_y = 0; -static unsigned char gr_current_r = 255; -static unsigned char gr_current_g = 255; -static unsigned char gr_current_b = 255; -static unsigned char gr_current_a = 255; +static uint32_t gr_current = ~0; +static constexpr uint32_t alpha_mask = 0xff000000; static GRSurface* gr_draw = NULL; +static GRRotation rotation = ROTATION_NONE; -static bool outside(int x, int y) -{ - return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; +static bool outside(int x, int y) { + return x < 0 || x >= (rotation % 2 ? gr_draw->height : gr_draw->width) || y < 0 || + y >= (rotation % 2 ? gr_draw->width : gr_draw->height); } -const GRFont* gr_sys_font() -{ - return gr_font; +const GRFont* gr_sys_font() { + return gr_font; } -int gr_measure(const GRFont* font, const char *s) -{ - return font->char_width * strlen(s); +int gr_measure(const GRFont* font, const char* s) { + return font->char_width * strlen(s); } -void gr_font_size(const GRFont* font, int *x, int *y) -{ - *x = font->char_width; - *y = font->char_height; +void gr_font_size(const GRFont* font, int* x, int* y) { + *x = font->char_width; + *y = font->char_height; } -static void text_blend(unsigned char* src_p, int src_row_bytes, - unsigned char* dst_p, int dst_row_bytes, - int width, int height) -{ - for (int j = 0; j < height; ++j) { - unsigned char* sx = src_p; - unsigned char* px = dst_p; - for (int i = 0; i < width; ++i) { - unsigned char a = *sx++; - if (gr_current_a < 255) a = ((int)a * gr_current_a) / 255; - if (a == 255) { - *px++ = gr_current_r; - *px++ = gr_current_g; - *px++ = gr_current_b; - px++; - } else if (a > 0) { - *px = (*px * (255-a) + gr_current_r * a) / 255; - ++px; - *px = (*px * (255-a) + gr_current_g * a) / 255; - ++px; - *px = (*px * (255-a) + gr_current_b * a) / 255; - ++px; - ++px; - } else { - px += 4; - } - } - src_p += src_row_bytes; - dst_p += dst_row_bytes; - } +// Blends gr_current onto pix value, assumes alpha as most significant byte. +static inline uint32_t pixel_blend(uint8_t alpha, uint32_t pix) { + if (alpha == 255) return gr_current; + if (alpha == 0) return pix; + uint32_t pix_r = pix & 0xff; + uint32_t pix_g = pix & 0xff00; + uint32_t pix_b = pix & 0xff0000; + uint32_t cur_r = gr_current & 0xff; + uint32_t cur_g = gr_current & 0xff00; + uint32_t cur_b = gr_current & 0xff0000; + + uint32_t out_r = (pix_r * (255 - alpha) + cur_r * alpha) / 255; + uint32_t out_g = (pix_g * (255 - alpha) + cur_g * alpha) / 255; + uint32_t out_b = (pix_b * (255 - alpha) + cur_b * alpha) / 255; + + return (out_r & 0xff) | (out_g & 0xff00) | (out_b & 0xff0000) | (gr_current & 0xff000000); } -void gr_text(const GRFont* font, int x, int y, const char *s, bool bold) -{ - if (!font->texture || gr_current_a == 0) return; +// increments pixel pointer right, with current rotation. +static void incr_x(uint32_t** p, int row_pixels) { + if (rotation % 2) { + *p = *p + (rotation == 1 ? 1 : -1) * row_pixels; + } else { + *p = *p + (rotation ? -1 : 1); + } +} - bold = bold && (font->texture->height != font->char_height); +// increments pixel pointer down, with current rotation. +static void incr_y(uint32_t** p, int row_pixels) { + if (rotation % 2) { + *p = *p + (rotation == 1 ? -1 : 1); + } else { + *p = *p + (rotation ? -1 : 1) * row_pixels; + } +} - x += overscan_offset_x; - y += overscan_offset_y; +// returns pixel pointer at given coordinates with rotation adjustment. +static uint32_t* pixel_at(GRSurface* surf, int x, int y, int row_pixels) { + switch (rotation) { + case ROTATION_NONE: + return reinterpret_cast(surf->data) + y * row_pixels + x; + case ROTATION_RIGHT: + return reinterpret_cast(surf->data) + x * row_pixels + (surf->width - y); + case ROTATION_DOWN: + return reinterpret_cast(surf->data) + (surf->height - 1 - y) * row_pixels + + (surf->width - 1 - x); + case ROTATION_LEFT: + return reinterpret_cast(surf->data) + (surf->height - 1 - x) * row_pixels + y; + default: + printf("invalid rotation %d", rotation); + } + return nullptr; +} - unsigned char ch; - while ((ch = *s++)) { - if (outside(x, y) || outside(x+font->char_width-1, y+font->char_height-1)) break; +static void text_blend(uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels, + int width, int height) { + uint8_t alpha_current = static_cast((alpha_mask & gr_current) >> 24); + for (int j = 0; j < height; ++j) { + uint8_t* sx = src_p; + uint32_t* px = dst_p; + for (int i = 0; i < width; ++i, incr_x(&px, dst_row_pixels)) { + uint8_t a = *sx++; + if (alpha_current < 255) a = (static_cast(a) * alpha_current) / 255; + *px = pixel_blend(a, *px); + } + src_p += src_row_bytes; + incr_y(&dst_p, dst_row_pixels); + } +} + +void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) { + if (!font || !font->texture || (gr_current & alpha_mask) == 0) return; + + if (font->texture->pixel_bytes != 1) { + printf("gr_text: font has wrong format\n"); + return; + } - if (ch < ' ' || ch > '~') { - ch = '?'; - } + bold = bold && (font->texture->height != font->char_height); - unsigned char* src_p = font->texture->data + ((ch - ' ') * font->char_width) + - (bold ? font->char_height * font->texture->row_bytes : 0); - unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes; + x += overscan_offset_x; + y += overscan_offset_y; - text_blend(src_p, font->texture->row_bytes, - dst_p, gr_draw->row_bytes, - font->char_width, font->char_height); + unsigned char ch; + while ((ch = *s++)) { + if (outside(x, y) || outside(x + font->char_width - 1, y + font->char_height - 1)) break; - x += font->char_width; + if (ch < ' ' || ch > '~') { + ch = '?'; } + + int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; + uint8_t* src_p = font->texture->data + ((ch - ' ') * font->char_width) + + (bold ? font->char_height * font->texture->row_bytes : 0); + uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels); + + text_blend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width, + font->char_height); + + x += font->char_width; + } } void gr_texticon(int x, int y, GRSurface* icon) { - if (icon == NULL) return; + if (icon == NULL) return; - if (icon->pixel_bytes != 1) { - printf("gr_texticon: source has wrong format\n"); - return; - } + if (icon->pixel_bytes != 1) { + printf("gr_texticon: source has wrong format\n"); + return; + } - x += overscan_offset_x; - y += overscan_offset_y; + x += overscan_offset_x; + y += overscan_offset_y; - if (outside(x, y) || outside(x+icon->width-1, y+icon->height-1)) return; + if (outside(x, y) || outside(x + icon->width - 1, y + icon->height - 1)) return; - unsigned char* src_p = icon->data; - unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes; + int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; + uint8_t* src_p = icon->data; + uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels); - text_blend(src_p, icon->row_bytes, - dst_p, gr_draw->row_bytes, - icon->width, icon->height); + text_blend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height); } -void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + uint32_t r32 = r, g32 = g, b32 = b, a32 = a; #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) - gr_current_r = b; - gr_current_g = g; - gr_current_b = r; - gr_current_a = a; + gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32; #else - gr_current_r = r; - gr_current_g = g; - gr_current_b = b; - gr_current_a = a; + gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32; #endif } -void gr_clear() -{ - if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) { - memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes); - } else { - unsigned char* px = gr_draw->data; - for (int y = 0; y < gr_draw->height; ++y) { - for (int x = 0; x < gr_draw->width; ++x) { - *px++ = gr_current_r; - *px++ = gr_current_g; - *px++ = gr_current_b; - px++; - } - px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes); - } +void gr_clear() { + if ((gr_current & 0xff) == ((gr_current >> 8) & 0xff) && + (gr_current & 0xff) == ((gr_current >> 16) & 0xff) && + (gr_current & 0xff) == ((gr_current >> 24) & 0xff) && + gr_draw->row_bytes == gr_draw->width * gr_draw->pixel_bytes) { + memset(gr_draw->data, gr_current & 0xff, gr_draw->height * gr_draw->row_bytes); + } else { + uint32_t* px = reinterpret_cast(gr_draw->data); + int row_diff = gr_draw->row_bytes / gr_draw->pixel_bytes - gr_draw->width; + for (int y = 0; y < gr_draw->height; ++y) { + for (int x = 0; x < gr_draw->width; ++x) { + *px++ = gr_current; + } + px += row_diff; } + } } -void gr_fill(int x1, int y1, int x2, int y2) -{ - x1 += overscan_offset_x; - y1 += overscan_offset_y; - - x2 += overscan_offset_x; - y2 += overscan_offset_y; - - if (outside(x1, y1) || outside(x2-1, y2-1)) return; - - unsigned char* p = gr_draw->data + y1 * gr_draw->row_bytes + x1 * gr_draw->pixel_bytes; - if (gr_current_a == 255) { - int x, y; - for (y = y1; y < y2; ++y) { - unsigned char* px = p; - for (x = x1; x < x2; ++x) { - *px++ = gr_current_r; - *px++ = gr_current_g; - *px++ = gr_current_b; - px++; - } - p += gr_draw->row_bytes; - } - } else if (gr_current_a > 0) { - int x, y; - for (y = y1; y < y2; ++y) { - unsigned char* px = p; - for (x = x1; x < x2; ++x) { - *px = (*px * (255-gr_current_a) + gr_current_r * gr_current_a) / 255; - ++px; - *px = (*px * (255-gr_current_a) + gr_current_g * gr_current_a) / 255; - ++px; - *px = (*px * (255-gr_current_a) + gr_current_b * gr_current_a) / 255; - ++px; - ++px; - } - p += gr_draw->row_bytes; - } +void gr_fill(int x1, int y1, int x2, int y2) { + x1 += overscan_offset_x; + y1 += overscan_offset_y; + + x2 += overscan_offset_x; + y2 += overscan_offset_y; + + if (outside(x1, y1) || outside(x2 - 1, y2 - 1)) return; + + int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; + uint32_t* p = pixel_at(gr_draw, x1, y1, row_pixels); + uint8_t alpha = static_cast(((gr_current & alpha_mask) >> 24)); + if (alpha > 0) { + for (int y = y1; y < y2; ++y) { + uint32_t* px = p; + for (int x = x1; x < x2; ++x) { + *px = pixel_blend(alpha, *px); + incr_x(&px, row_pixels); + } + incr_y(&p, row_pixels); } + } } void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { - if (source == NULL) return; - - if (gr_draw->pixel_bytes != source->pixel_bytes) { - printf("gr_blit: source has wrong format\n"); - return; - } - - dx += overscan_offset_x; - dy += overscan_offset_y; + if (source == NULL) return; - if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return; + if (gr_draw->pixel_bytes != source->pixel_bytes) { + printf("gr_blit: source has wrong format\n"); + return; + } - unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes; - unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes; + dx += overscan_offset_x; + dy += overscan_offset_y; + + if (outside(dx, dy) || outside(dx + w - 1, dy + h - 1)) return; + + if (rotation) { + int src_row_pixels = source->row_bytes / source->pixel_bytes; + int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; + uint32_t* src_py = reinterpret_cast(source->data) + sy * source->row_bytes / 4 + sx; + uint32_t* dst_py = pixel_at(gr_draw, dx, dy, row_pixels); + + for (int y = 0; y < h; y += 1) { + uint32_t* src_px = src_py; + uint32_t* dst_px = dst_py; + for (int x = 0; x < w; x += 1) { + *dst_px = *src_px++; + incr_x(&dst_px, row_pixels); + } + src_py += src_row_pixels; + incr_y(&dst_py, row_pixels); + } + } else { + unsigned char* src_p = source->data + sy * source->row_bytes + sx * source->pixel_bytes; + unsigned char* dst_p = gr_draw->data + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes; int i; for (i = 0; i < h; ++i) { - memcpy(dst_p, src_p, w * source->pixel_bytes); - src_p += source->row_bytes; - dst_p += gr_draw->row_bytes; + memcpy(dst_p, src_p, w * source->pixel_bytes); + src_p += source->row_bytes; + dst_p += gr_draw->row_bytes; } + } } unsigned int gr_get_width(GRSurface* surface) { - if (surface == NULL) { - return 0; - } - return surface->width; + if (surface == NULL) { + return 0; + } + return surface->width; } unsigned int gr_get_height(GRSurface* surface) { - if (surface == NULL) { - return 0; - } - return surface->height; + if (surface == NULL) { + return 0; + } + return surface->height; } int gr_init_font(const char* name, GRFont** dest) { - GRFont* font = static_cast(calloc(1, sizeof(*gr_font))); - if (font == nullptr) { - return -1; - } + GRFont* font = static_cast(calloc(1, sizeof(*gr_font))); + if (font == nullptr) { + return -1; + } - int res = res_create_alpha_surface(name, &(font->texture)); - if (res < 0) { - free(font); - return res; - } + int res = res_create_alpha_surface(name, &(font->texture)); + if (res < 0) { + free(font); + return res; + } - // The font image should be a 96x2 array of character images. The - // columns are the printable ASCII characters 0x20 - 0x7f. The - // top row is regular text; the bottom row is bold. - font->char_width = font->texture->width / 96; - font->char_height = font->texture->height / 2; + // The font image should be a 96x2 array of character images. The + // columns are the printable ASCII characters 0x20 - 0x7f. The + // top row is regular text; the bottom row is bold. + font->char_width = font->texture->width / 96; + font->char_height = font->texture->height / 2; - *dest = font; + *dest = font; - return 0; + return 0; } -static void gr_init_font(void) -{ - int res = gr_init_font("font", &gr_font); - if (res == 0) { - return; - } - - printf("failed to read font: res=%d\n", res); +static void gr_init_font(void) { + int res = gr_init_font("font", &gr_font); + if (res == 0) { + return; + } + printf("failed to read font: res=%d\n", res); - // fall back to the compiled-in font. - gr_font = static_cast(calloc(1, sizeof(*gr_font))); - gr_font->texture = static_cast(malloc(sizeof(*gr_font->texture))); - gr_font->texture->width = font.width; - gr_font->texture->height = font.height; - gr_font->texture->row_bytes = font.width; - gr_font->texture->pixel_bytes = 1; + // fall back to the compiled-in font. + gr_font = static_cast(calloc(1, sizeof(*gr_font))); + gr_font->texture = static_cast(malloc(sizeof(*gr_font->texture))); + gr_font->texture->width = font.width; + gr_font->texture->height = font.height; + gr_font->texture->row_bytes = font.width; + gr_font->texture->pixel_bytes = 1; - unsigned char* bits = static_cast(malloc(font.width * font.height)); - gr_font->texture->data = bits; + unsigned char* bits = static_cast(malloc(font.width * font.height)); + gr_font->texture->data = bits; - unsigned char data; - unsigned char* in = font.rundata; - while((data = *in++)) { - memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f); - bits += (data & 0x7f); - } + unsigned char data; + unsigned char* in = font.rundata; + while ((data = *in++)) { + memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f); + bits += (data & 0x7f); + } - gr_font->char_width = font.char_width; - gr_font->char_height = font.char_height; + gr_font->char_width = font.char_width; + gr_font->char_height = font.char_height; } void gr_flip() { @@ -344,6 +375,12 @@ int gr_init() { gr_flip(); gr_flip(); + gr_rotate(DEFAULT_ROTATION); + + if (gr_draw->pixel_bytes != 4) { + printf("gr_init: Only 4-byte pixel formats supported\n"); + } + return 0; } @@ -352,13 +389,19 @@ void gr_exit() { } int gr_fb_width() { - return gr_draw->width - 2 * overscan_offset_x; + return rotation % 2 ? gr_draw->height - 2 * overscan_offset_y + : gr_draw->width - 2 * overscan_offset_x; } int gr_fb_height() { - return gr_draw->height - 2 * overscan_offset_y; + return rotation % 2 ? gr_draw->width - 2 * overscan_offset_x + : gr_draw->height - 2 * overscan_offset_y; } void gr_fb_blank(bool blank) { gr_backend->Blank(blank); } + +void gr_rotate(GRRotation rot) { + rotation = rot; +} diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index 27e603136..f9da19999 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -28,17 +28,24 @@ // struct GRSurface { - int width; - int height; - int row_bytes; - int pixel_bytes; - unsigned char* data; + int width; + int height; + int row_bytes; + int pixel_bytes; + unsigned char* data; }; struct GRFont { - GRSurface* texture; - int char_width; - int char_height; + GRSurface* texture; + int char_width; + int char_height; +}; + +enum GRRotation { + ROTATION_NONE = 0, + ROTATION_RIGHT = 1, + ROTATION_DOWN = 2, + ROTATION_LEFT = 3, }; int gr_init(); @@ -58,14 +65,17 @@ void gr_texticon(int x, int y, GRSurface* icon); const GRFont* gr_sys_font(); int gr_init_font(const char* name, GRFont** dest); -void gr_text(const GRFont* font, int x, int y, const char *s, bool bold); -int gr_measure(const GRFont* font, const char *s); -void gr_font_size(const GRFont* font, int *x, int *y); +void gr_text(const GRFont* font, int x, int y, const char* s, bool bold); +int gr_measure(const GRFont* font, const char* s); +void gr_font_size(const GRFont* font, int* x, int* y); void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy); unsigned int gr_get_width(GRSurface* surface); unsigned int gr_get_height(GRSurface* surface); +// Set rotation, flips gr_fb_width/height if 90 degree rotation difference +void gr_rotate(GRRotation rotation); + // // Input events. // @@ -115,8 +125,8 @@ int res_create_display_surface(const char* name, GRSurface** pSurface); // should have a 'Frames' text chunk whose value is the number of // frames this image represents. The pixel data itself is interlaced // by row. -int res_create_multi_display_surface(const char* name, int* frames, - int* fps, GRSurface*** pSurface); +int res_create_multi_display_surface(const char* name, int* frames, int* fps, + GRSurface*** pSurface); // Load a single alpha surface from a grayscale PNG image. int res_create_alpha_surface(const char* name, GRSurface** pSurface); -- cgit v1.2.3 From acba38c2889d210097b76cad1bb0fba9e027a620 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 27 Sep 2017 17:53:34 -0700 Subject: Move the png open and destroy functions into a class The open_png() function used to open the png file but didn't close it; and this caused the leak of fd. However, we cannot close the file inside open_png() because the png file needs to remain open until the outer function finishes parsing the file and destroys the png struct. This CL addresses this issue by implementing a PngReader class to handle the creation/destruction of the png struct. Bug: 67010912 Test: Run graphic tests; also run locale tests and check fd. Change-Id: I9a803b3cd8c16f16a9ffe8f0acc7fe0f42e95eb0 --- minui/resources.cpp | 565 ++++++++++++++++++++++++++-------------------------- 1 file changed, 279 insertions(+), 286 deletions(-) diff --git a/minui/resources.cpp b/minui/resources.cpp index 756f29d21..837f5ebca 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -25,10 +25,12 @@ #include #include +#include #include #include #include +#include #include #include @@ -46,89 +48,126 @@ static GRSurface* malloc_surface(size_t data_size) { return surface; } -static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr, - png_uint_32* width, png_uint_32* height, png_byte* channels) { - char resPath[256]; - unsigned char header[8]; - int result = 0; - int color_type, bit_depth; - size_t bytesRead; - - snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); - resPath[sizeof(resPath)-1] = '\0'; - FILE* fp = fopen(resPath, "rbe"); - if (fp == NULL) { - result = -1; - goto exit; - } +// This class handles the png file parsing. It also holds the ownership of the png pointer and the +// opened file pointer. Both will be destroyed/closed when this object goes out of scope. +class PngHandler { + public: + PngHandler(const std::string& name); - bytesRead = fread(header, 1, sizeof(header), fp); - if (bytesRead != sizeof(header)) { - result = -2; - goto exit; - } + ~PngHandler(); - if (png_sig_cmp(header, 0, sizeof(header))) { - result = -3; - goto exit; - } + png_uint_32 width() const { + return width_; + } - *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!*png_ptr) { - result = -4; - goto exit; - } + png_uint_32 height() const { + return height_; + } - *info_ptr = png_create_info_struct(*png_ptr); - if (!*info_ptr) { - result = -5; - goto exit; - } + png_byte channels() const { + return channels_; + } - if (setjmp(png_jmpbuf(*png_ptr))) { - result = -6; - goto exit; - } + png_structp png_ptr() const { + return png_ptr_; + } - png_init_io(*png_ptr, fp); - png_set_sig_bytes(*png_ptr, sizeof(header)); - png_read_info(*png_ptr, *info_ptr); - - png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth, - &color_type, NULL, NULL, NULL); - - *channels = png_get_channels(*png_ptr, *info_ptr); - - if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) { - // 8-bit RGB images: great, nothing to do. - } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) { - // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray. - png_set_expand_gray_1_2_4_to_8(*png_ptr); - } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) { - // paletted images: expand to 8-bit RGB. Note that we DON'T - // currently expand the tRNS chunk (if any) to an alpha - // channel, because minui doesn't support alpha channels in - // general. - png_set_palette_to_rgb(*png_ptr); - *channels = 3; - } else { - fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", - bit_depth, *channels, color_type); - result = -7; - goto exit; - } + png_infop info_ptr() const { + return info_ptr_; + } - return result; + int error_code() const { + return error_code_; + }; - exit: - if (result < 0) { - png_destroy_read_struct(png_ptr, info_ptr, NULL); - } - if (fp != NULL) { - fclose(fp); - } + operator bool() const { + return error_code_ == 0; + } + + private: + png_structp png_ptr_{ nullptr }; + png_infop info_ptr_{ nullptr }; + png_uint_32 width_; + png_uint_32 height_; + png_byte channels_; + + // The |error_code_| is set to a negative value if an error occurs when opening the png file. + int error_code_; + // After initialization, we'll keep the file pointer open before destruction of PngHandler. + std::unique_ptr png_fp_; +}; + +PngHandler::PngHandler(const std::string& name) : error_code_(0), png_fp_(nullptr, fclose) { + std::string res_path = android::base::StringPrintf("/res/images/%s.png", name.c_str()); + png_fp_.reset(fopen(res_path.c_str(), "rbe")); + if (!png_fp_) { + error_code_ = -1; + return; + } + + unsigned char header[8]; + size_t bytesRead = fread(header, 1, sizeof(header), png_fp_.get()); + if (bytesRead != sizeof(header)) { + error_code_ = -2; + return; + } + + if (png_sig_cmp(header, 0, sizeof(header))) { + error_code_ = -3; + return; + } - return result; + png_ptr_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_ptr_) { + error_code_ = -4; + return; + } + + info_ptr_ = png_create_info_struct(png_ptr_); + if (!info_ptr_) { + error_code_ = -5; + return; + } + + if (setjmp(png_jmpbuf(png_ptr_))) { + error_code_ = -6; + return; + } + + png_init_io(png_ptr_, png_fp_.get()); + png_set_sig_bytes(png_ptr_, sizeof(header)); + png_read_info(png_ptr_, info_ptr_); + + int color_type; + int bit_depth; + png_get_IHDR(png_ptr_, info_ptr_, &width_, &height_, &bit_depth, &color_type, nullptr, nullptr, + nullptr); + + channels_ = png_get_channels(png_ptr_, info_ptr_); + + if (bit_depth == 8 && channels_ == 3 && color_type == PNG_COLOR_TYPE_RGB) { + // 8-bit RGB images: great, nothing to do. + } else if (bit_depth <= 8 && channels_ == 1 && color_type == PNG_COLOR_TYPE_GRAY) { + // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray. + png_set_expand_gray_1_2_4_to_8(png_ptr_); + } else if (bit_depth <= 8 && channels_ == 1 && color_type == PNG_COLOR_TYPE_PALETTE) { + // paletted images: expand to 8-bit RGB. Note that we DON'T + // currently expand the tRNS chunk (if any) to an alpha + // channel, because minui doesn't support alpha channels in + // general. + png_set_palette_to_rgb(png_ptr_); + channels_ = 3; + } else { + fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", bit_depth, + channels_, color_type); + error_code_ = -7; + } +} + +PngHandler::~PngHandler() { + if (png_ptr_) { + png_destroy_read_struct(&png_ptr_, &info_ptr_, nullptr); + } } // "display" surfaces are transformed into the framebuffer's required @@ -198,178 +237,152 @@ static void transform_rgb_to_draw(unsigned char* input_row, } int res_create_display_surface(const char* name, GRSurface** pSurface) { - GRSurface* surface = NULL; - int result = 0; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - png_uint_32 width, height; - png_byte channels; - unsigned char* p_row; - unsigned int y; - - *pSurface = NULL; - - result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); - if (result < 0) return result; - - surface = init_display_surface(width, height); - if (surface == NULL) { - result = -8; - goto exit; - } + *pSurface = nullptr; + + PngHandler png_handler(name); + if (!png_handler) return png_handler.error_code(); + + png_structp png_ptr = png_handler.png_ptr(); + png_uint_32 width = png_handler.width(); + png_uint_32 height = png_handler.height(); + + GRSurface* surface = init_display_surface(width, height); + if (!surface) { + return -8; + } #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) - png_set_bgr(png_ptr); + png_set_bgr(png_ptr); #endif - p_row = static_cast(malloc(width * 4)); - for (y = 0; y < height; ++y) { - png_read_row(png_ptr, p_row, NULL); - transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width); - } - free(p_row); + for (png_uint_32 y = 0; y < height; ++y) { + std::vector p_row(width * 4); + png_read_row(png_ptr, p_row.data(), nullptr); + transform_rgb_to_draw(p_row.data(), surface->data + y * surface->row_bytes, + png_handler.channels(), width); + } - *pSurface = surface; + *pSurface = surface; - exit: - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - if (result < 0 && surface != NULL) free(surface); - return result; + return 0; } int res_create_multi_display_surface(const char* name, int* frames, int* fps, - GRSurface*** pSurface) { - GRSurface** surface = NULL; - int result = 0; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - png_uint_32 width, height; - png_byte channels; - png_textp text; - int num_text; - unsigned char* p_row; - unsigned int y; - - *pSurface = NULL; - *frames = -1; - - result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); - if (result < 0) return result; - - *frames = 1; - *fps = 20; - if (png_get_text(png_ptr, info_ptr, &text, &num_text)) { - for (int i = 0; i < num_text; ++i) { - if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) { - *frames = atoi(text[i].text); - } else if (text[i].key && strcmp(text[i].key, "FPS") == 0 && text[i].text) { - *fps = atoi(text[i].text); - } - } - printf(" found frames = %d\n", *frames); - printf(" found fps = %d\n", *fps); + GRSurface*** pSurface) { + *pSurface = nullptr; + *frames = -1; + + PngHandler png_handler(name); + if (!png_handler) return png_handler.error_code(); + + png_structp png_ptr = png_handler.png_ptr(); + png_uint_32 width = png_handler.width(); + png_uint_32 height = png_handler.height(); + + *frames = 1; + *fps = 20; + png_textp text; + int num_text; + if (png_get_text(png_ptr, png_handler.info_ptr(), &text, &num_text)) { + for (int i = 0; i < num_text; ++i) { + if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) { + *frames = atoi(text[i].text); + } else if (text[i].key && strcmp(text[i].key, "FPS") == 0 && text[i].text) { + *fps = atoi(text[i].text); + } } + printf(" found frames = %d\n", *frames); + printf(" found fps = %d\n", *fps); + } - if (*frames <= 0 || *fps <= 0) { - printf("bad number of frames (%d) and/or FPS (%d)\n", *frames, *fps); - result = -10; - goto exit; - } + int result = 0; + GRSurface** surface = nullptr; + if (*frames <= 0 || *fps <= 0) { + printf("bad number of frames (%d) and/or FPS (%d)\n", *frames, *fps); + result = -10; + goto exit; + } - if (height % *frames != 0) { - printf("bad height (%d) for frame count (%d)\n", height, *frames); - result = -9; - goto exit; - } + if (height % *frames != 0) { + printf("bad height (%d) for frame count (%d)\n", height, *frames); + result = -9; + goto exit; + } - surface = static_cast(calloc(*frames, sizeof(GRSurface*))); - if (surface == NULL) { - result = -8; - goto exit; - } - for (int i = 0; i < *frames; ++i) { - surface[i] = init_display_surface(width, height / *frames); - if (surface[i] == NULL) { - result = -8; - goto exit; - } + surface = static_cast(calloc(*frames, sizeof(GRSurface*))); + if (!surface) { + result = -8; + goto exit; + } + for (int i = 0; i < *frames; ++i) { + surface[i] = init_display_surface(width, height / *frames); + if (!surface[i]) { + result = -8; + goto exit; } + } #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) - png_set_bgr(png_ptr); + png_set_bgr(png_ptr); #endif - p_row = static_cast(malloc(width * 4)); - for (y = 0; y < height; ++y) { - png_read_row(png_ptr, p_row, NULL); - int frame = y % *frames; - unsigned char* out_row = surface[frame]->data + - (y / *frames) * surface[frame]->row_bytes; - transform_rgb_to_draw(p_row, out_row, channels, width); - } - free(p_row); + for (png_uint_32 y = 0; y < height; ++y) { + std::vector p_row(width * 4); + png_read_row(png_ptr, p_row.data(), nullptr); + int frame = y % *frames; + unsigned char* out_row = surface[frame]->data + (y / *frames) * surface[frame]->row_bytes; + transform_rgb_to_draw(p_row.data(), out_row, png_handler.channels(), width); + } - *pSurface = surface; + *pSurface = surface; exit: - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - - if (result < 0) { - if (surface) { - for (int i = 0; i < *frames; ++i) { - free(surface[i]); - } - free(surface); - } + if (result < 0) { + if (surface) { + for (int i = 0; i < *frames; ++i) { + free(surface[i]); + } + free(surface); } - return result; + } + return result; } int res_create_alpha_surface(const char* name, GRSurface** pSurface) { - GRSurface* surface = NULL; - int result = 0; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - png_uint_32 width, height; - png_byte channels; + *pSurface = nullptr; - *pSurface = NULL; + PngHandler png_handler(name); + if (!png_handler) return png_handler.error_code(); - result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); - if (result < 0) return result; + if (png_handler.channels() != 1) { + return -7; + } - if (channels != 1) { - result = -7; - goto exit; - } + png_structp png_ptr = png_handler.png_ptr(); + png_uint_32 width = png_handler.width(); + png_uint_32 height = png_handler.height(); - surface = malloc_surface(width * height); - if (surface == NULL) { - result = -8; - goto exit; - } - surface->width = width; - surface->height = height; - surface->row_bytes = width; - surface->pixel_bytes = 1; + GRSurface* surface = malloc_surface(width * height); + if (!surface) { + return -8; + } + surface->width = width; + surface->height = height; + surface->row_bytes = width; + surface->pixel_bytes = 1; #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA) - png_set_bgr(png_ptr); + png_set_bgr(png_ptr); #endif - unsigned char* p_row; - unsigned int y; - for (y = 0; y < height; ++y) { - p_row = surface->data + y * surface->row_bytes; - png_read_row(png_ptr, p_row, NULL); - } + for (png_uint_32 y = 0; y < height; ++y) { + unsigned char* p_row = surface->data + y * surface->row_bytes; + png_read_row(png_ptr, p_row, nullptr); + } - *pSurface = surface; + *pSurface = surface; - exit: - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - if (result < 0 && surface != NULL) free(surface); - return result; + return 0; } // This function tests if a locale string stored in PNG (prefix) matches @@ -397,109 +410,89 @@ bool matches_locale(const std::string& prefix, const std::string& locale) { } std::vector get_locales_in_png(const std::string& png_name) { - png_structp png_ptr = nullptr; - png_infop info_ptr = nullptr; - png_uint_32 width, height; - png_byte channels; - - int status = open_png(png_name.c_str(), &png_ptr, &info_ptr, &width, &height, &channels); - if (status < 0) { - printf("Failed to open %s\n", png_name.c_str()); + PngHandler png_handler(png_name); + if (!png_handler) { + printf("Failed to open %s, error: %d\n", png_name.c_str(), png_handler.error_code()); return {}; } - if (channels != 1) { - printf("Expect input png to have 1 data channel, this file has %d\n", channels); - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + if (png_handler.channels() != 1) { + printf("Expect input png to have 1 data channel, this file has %d\n", png_handler.channels()); return {}; } std::vector result; - std::vector row(width); - for (png_uint_32 y = 0; y < height; ++y) { - png_read_row(png_ptr, row.data(), nullptr); + std::vector row(png_handler.width()); + for (png_uint_32 y = 0; y < png_handler.height(); ++y) { + png_read_row(png_handler.png_ptr(), row.data(), nullptr); int h = (row[3] << 8) | row[2]; std::string loc(reinterpret_cast(&row[5])); if (!loc.empty()) { result.push_back(loc); } for (int i = 0; i < h; ++i, ++y) { - png_read_row(png_ptr, row.data(), NULL); + png_read_row(png_handler.png_ptr(), row.data(), nullptr); } } - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); return result; } int res_create_localized_alpha_surface(const char* name, const char* locale, GRSurface** pSurface) { - GRSurface* surface = NULL; - int result = 0; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - png_uint_32 width, height; - png_byte channels; - png_uint_32 y; - std::vector row; - - *pSurface = NULL; - - if (locale == NULL) { - return result; - } + *pSurface = nullptr; + if (locale == nullptr) { + return 0; + } - result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); - if (result < 0) return result; + PngHandler png_handler(name); + if (!png_handler) return png_handler.error_code(); - if (channels != 1) { - result = -7; - goto exit; - } + if (png_handler.channels() != 1) { + return -7; + } - row.resize(width); - for (y = 0; y < height; ++y) { - png_read_row(png_ptr, row.data(), NULL); - int w = (row[1] << 8) | row[0]; - int h = (row[3] << 8) | row[2]; - __unused int len = row[4]; - char* loc = reinterpret_cast(&row[5]); - - if (y+1+h >= height || matches_locale(loc, locale)) { - printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); - - surface = malloc_surface(w*h); - if (surface == NULL) { - result = -8; - goto exit; - } - surface->width = w; - surface->height = h; - surface->row_bytes = w; - surface->pixel_bytes = 1; - - int i; - for (i = 0; i < h; ++i, ++y) { - png_read_row(png_ptr, row.data(), NULL); - memcpy(surface->data + i*w, row.data(), w); - } + png_structp png_ptr = png_handler.png_ptr(); + png_uint_32 width = png_handler.width(); + png_uint_32 height = png_handler.height(); - *pSurface = surface; - break; - } else { - int i; - for (i = 0; i < h; ++i, ++y) { - png_read_row(png_ptr, row.data(), NULL); - } - } + for (png_uint_32 y = 0; y < height; ++y) { + std::vector row(width); + png_read_row(png_ptr, row.data(), nullptr); + int w = (row[1] << 8) | row[0]; + int h = (row[3] << 8) | row[2]; + __unused int len = row[4]; + char* loc = reinterpret_cast(&row[5]); + + if (y + 1 + h >= height || matches_locale(loc, locale)) { + printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); + + GRSurface* surface = malloc_surface(w * h); + if (!surface) { + return -8; + } + surface->width = w; + surface->height = h; + surface->row_bytes = w; + surface->pixel_bytes = 1; + + for (int i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row.data(), nullptr); + memcpy(surface->data + i * w, row.data(), w); + } + + *pSurface = surface; + break; } -exit: - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - if (result < 0 && surface != NULL) free(surface); - return result; + for (int i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row.data(), nullptr); + } + } + + return 0; } void res_free_surface(GRSurface* surface) { - free(surface); + free(surface); } -- cgit v1.2.3 From ac27a7a98733ea2444eedacfd2d8dd4a384726f6 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 28 Sep 2017 17:43:53 -0700 Subject: otafault: Clean up header inclusion. Remove unneeded #includes. Also remove unneeded dependency on libz and libselinux. Test: mmma bootable/recovery Change-Id: Ic8f7f46f4b89762dee384921504489de75320ac0 --- otafault/Android.mk | 2 -- otafault/config.cpp | 6 ++---- otafault/config.h | 20 +++++++++----------- otafault/ota_io.cpp | 4 +++- otafault/ota_io.h | 3 ++- otafault/test.cpp | 3 ++- 6 files changed, 18 insertions(+), 20 deletions(-) diff --git a/otafault/Android.mk b/otafault/Android.mk index 4784d56ef..383717612 100644 --- a/otafault/Android.mk +++ b/otafault/Android.mk @@ -18,8 +18,6 @@ include $(CLEAR_VARS) otafault_static_libs := \ libziparchive \ - libz \ - libselinux \ libbase \ liblog diff --git a/otafault/config.cpp b/otafault/config.cpp index b94e429c6..c11f77452 100644 --- a/otafault/config.cpp +++ b/otafault/config.cpp @@ -14,16 +14,14 @@ * limitations under the License. */ +#include "config.h" + #include #include -#include -#include - #include #include -#include "config.h" #include "ota_io.h" #define OTAIO_MAX_FNAME_SIZE 128 diff --git a/otafault/config.h b/otafault/config.h index 4adbdd121..cc4bfd2ad 100644 --- a/otafault/config.h +++ b/otafault/config.h @@ -15,13 +15,13 @@ */ /* - * Read configuration files in the OTA package to determine which files, if any, will trigger errors. + * Read configuration files in the OTA package to determine which files, if any, will trigger + * errors. * - * OTA packages can be modified to trigger errors by adding a top-level - * directory called .libotafault, which may optionally contain up to three - * files called READ, WRITE, and FSYNC. Each one of these optional files - * contains the name of a single file on the device disk which will cause - * an IO error on the first call of the appropriate I/O action to that file. + * OTA packages can be modified to trigger errors by adding a top-level directory called + * .libotafault, which may optionally contain up to three files called READ, WRITE, and FSYNC. + * Each one of these optional files contains the name of a single file on the device disk which + * will cause an IO error on the first call of the appropriate I/O action to that file. * * Example: * ota.zip @@ -29,9 +29,9 @@ * .libotafault * WRITE * - * If the contents of the file WRITE were /system/build.prop, the first write - * action to /system/build.prop would fail with EIO. Note that READ and - * FSYNC files are absent, so these actions will not cause an error. + * If the contents of the file WRITE were /system/build.prop, the first write action to + * /system/build.prop would fail with EIO. Note that READ and FSYNC files are absent, so these + * actions will not cause an error. */ #ifndef _UPDATER_OTA_IO_CFG_H_ @@ -39,8 +39,6 @@ #include -#include - #include #define OTAIO_BASE_DIR ".libotafault" diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp index faae5275d..a82a7ee59 100644 --- a/otafault/ota_io.cpp +++ b/otafault/ota_io.cpp @@ -18,15 +18,17 @@ #include #include +#include #include #include +#include #include #include -#include #include #include + #include "config.h" static std::mutex filename_mutex; diff --git a/otafault/ota_io.h b/otafault/ota_io.h index 9428f1b1f..45e481a62 100644 --- a/otafault/ota_io.h +++ b/otafault/ota_io.h @@ -23,8 +23,9 @@ #ifndef _UPDATER_OTA_IO_H_ #define _UPDATER_OTA_IO_H_ +#include #include -#include +#include // mode_t #include diff --git a/otafault/test.cpp b/otafault/test.cpp index 6514782bf..60c40e099 100644 --- a/otafault/test.cpp +++ b/otafault/test.cpp @@ -14,9 +14,10 @@ * limitations under the License. */ -#include #include #include +#include +#include #include #include "ota_io.h" -- cgit v1.2.3 From 646b05a66c52502835d7c301b68b2ebfc4af7e41 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 28 Sep 2017 17:43:53 -0700 Subject: otafault: Remove the use of LOCAL_WHOLE_STATIC_LIBRARIES. Commit d80a99883d5ae2b117c54f076fe1df7eae86d2f8 has explanation of potential issues. Test: mmma bootable/recovery Change-Id: I25ca9920952b7bbdd8a661d9dc90962431410bc4 --- applypatch/Android.mk | 6 ++++-- otafault/Android.mk | 2 +- tests/Android.mk | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 59aa0ce6c..f5dda2bc4 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -112,11 +112,13 @@ LOCAL_C_INCLUDES := bootable/recovery LOCAL_STATIC_LIBRARIES := \ libapplypatch_modes \ libapplypatch \ - libbase \ libedify \ libotafault \ - libcrypto \ libbspatch \ + libbase \ + libziparchive \ + liblog \ + libcrypto \ libbz LOCAL_SHARED_LIBRARIES := \ libbase \ diff --git a/otafault/Android.mk b/otafault/Android.mk index 383717612..3e14f77f3 100644 --- a/otafault/Android.mk +++ b/otafault/Android.mk @@ -33,7 +33,7 @@ LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libotafault LOCAL_C_INCLUDES := bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) -LOCAL_WHOLE_STATIC_LIBRARIES := $(otafault_static_libs) +LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) include $(BUILD_STATIC_LIBRARY) diff --git a/tests/Android.mk b/tests/Android.mk index 748d9c87b..31c7de177 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -143,7 +143,6 @@ LOCAL_STATIC_LIBRARIES := \ libdivsufsort \ libdivsufsort64 \ libfs_mgr \ - liblog \ libvintf_recovery \ libvintf \ libtinyxml2 \ @@ -154,6 +153,7 @@ LOCAL_STATIC_LIBRARIES := \ libcrypto \ libbz \ libziparchive \ + liblog \ libutils \ libz \ libbase \ -- cgit v1.2.3 From d33b2f86b79573ccf73fd211d0813b2663fafd38 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 28 Sep 2017 21:29:11 -0700 Subject: otafault: Move headers under otafault/. Test: mmma bootable/recovery Change-Id: I3ceb72f703c7c2857d656c137d71baa1fccd8238 --- applypatch/applypatch.cpp | 2 +- otafault/Android.mk | 11 +++--- otafault/config.cpp | 4 +-- otafault/config.h | 72 -------------------------------------- otafault/include/otafault/config.h | 72 ++++++++++++++++++++++++++++++++++++++ otafault/include/otafault/ota_io.h | 70 ++++++++++++++++++++++++++++++++++++ otafault/ota_io.cpp | 4 +-- otafault/ota_io.h | 70 ------------------------------------ otafault/test.cpp | 2 +- updater/blockimg.cpp | 2 +- updater/install.cpp | 2 +- updater/updater.cpp | 2 +- 12 files changed, 157 insertions(+), 156 deletions(-) delete mode 100644 otafault/config.h create mode 100644 otafault/include/otafault/config.h create mode 100644 otafault/include/otafault/ota_io.h delete mode 100644 otafault/ota_io.h diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 51bf3932a..729d2a910 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -39,7 +39,7 @@ #include #include "edify/expr.h" -#include "ota_io.h" +#include "otafault/ota_io.h" #include "print_sha1.h" static int LoadPartitionContents(const std::string& filename, FileContents* file); diff --git a/otafault/Android.mk b/otafault/Android.mk index 3e14f77f3..5ec8a192a 100644 --- a/otafault/Android.mk +++ b/otafault/Android.mk @@ -31,8 +31,8 @@ LOCAL_CFLAGS := \ LOCAL_SRC_FILES := config.cpp ota_io.cpp LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libotafault -LOCAL_C_INCLUDES := bootable/recovery -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) include $(BUILD_STATIC_LIBRARY) @@ -41,12 +41,13 @@ include $(BUILD_STATIC_LIBRARY) # =============================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := config.cpp ota_io.cpp test.cpp +LOCAL_SRC_FILES := test.cpp LOCAL_MODULE_TAGS := tests LOCAL_MODULE := otafault_test -LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) +LOCAL_STATIC_LIBRARIES := \ + libotafault \ + $(otafault_static_libs) LOCAL_CFLAGS := -Wall -Werror -LOCAL_C_INCLUDES := bootable/recovery LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) diff --git a/otafault/config.cpp b/otafault/config.cpp index c11f77452..3993948ff 100644 --- a/otafault/config.cpp +++ b/otafault/config.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "config.h" +#include "otafault/config.h" #include #include @@ -22,7 +22,7 @@ #include #include -#include "ota_io.h" +#include "otafault/ota_io.h" #define OTAIO_MAX_FNAME_SIZE 128 diff --git a/otafault/config.h b/otafault/config.h deleted file mode 100644 index cc4bfd2ad..000000000 --- a/otafault/config.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Read configuration files in the OTA package to determine which files, if any, will trigger - * errors. - * - * OTA packages can be modified to trigger errors by adding a top-level directory called - * .libotafault, which may optionally contain up to three files called READ, WRITE, and FSYNC. - * Each one of these optional files contains the name of a single file on the device disk which - * will cause an IO error on the first call of the appropriate I/O action to that file. - * - * Example: - * ota.zip - * - * .libotafault - * WRITE - * - * If the contents of the file WRITE were /system/build.prop, the first write action to - * /system/build.prop would fail with EIO. Note that READ and FSYNC files are absent, so these - * actions will not cause an error. - */ - -#ifndef _UPDATER_OTA_IO_CFG_H_ -#define _UPDATER_OTA_IO_CFG_H_ - -#include - -#include - -#define OTAIO_BASE_DIR ".libotafault" -#define OTAIO_READ "READ" -#define OTAIO_WRITE "WRITE" -#define OTAIO_FSYNC "FSYNC" -#define OTAIO_CACHE "CACHE" - -/* - * Initialize libotafault by providing a reference to the OTA package. - */ -void ota_io_init(ZipArchiveHandle zip, bool retry); - -/* - * Return true if a config file is present for the given IO type. - */ -bool should_fault_inject(const char* io_type); - -/* - * Return true if an EIO should occur on the next hit to /cache/saved.file - * instead of the next hit to the specified file. - */ -bool should_hit_cache(); - -/* - * Return the name of the file that should cause an error for the - * given IO type. - */ -std::string fault_fname(const char* io_type); - -#endif diff --git a/otafault/include/otafault/config.h b/otafault/include/otafault/config.h new file mode 100644 index 000000000..cc4bfd2ad --- /dev/null +++ b/otafault/include/otafault/config.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Read configuration files in the OTA package to determine which files, if any, will trigger + * errors. + * + * OTA packages can be modified to trigger errors by adding a top-level directory called + * .libotafault, which may optionally contain up to three files called READ, WRITE, and FSYNC. + * Each one of these optional files contains the name of a single file on the device disk which + * will cause an IO error on the first call of the appropriate I/O action to that file. + * + * Example: + * ota.zip + * + * .libotafault + * WRITE + * + * If the contents of the file WRITE were /system/build.prop, the first write action to + * /system/build.prop would fail with EIO. Note that READ and FSYNC files are absent, so these + * actions will not cause an error. + */ + +#ifndef _UPDATER_OTA_IO_CFG_H_ +#define _UPDATER_OTA_IO_CFG_H_ + +#include + +#include + +#define OTAIO_BASE_DIR ".libotafault" +#define OTAIO_READ "READ" +#define OTAIO_WRITE "WRITE" +#define OTAIO_FSYNC "FSYNC" +#define OTAIO_CACHE "CACHE" + +/* + * Initialize libotafault by providing a reference to the OTA package. + */ +void ota_io_init(ZipArchiveHandle zip, bool retry); + +/* + * Return true if a config file is present for the given IO type. + */ +bool should_fault_inject(const char* io_type); + +/* + * Return true if an EIO should occur on the next hit to /cache/saved.file + * instead of the next hit to the specified file. + */ +bool should_hit_cache(); + +/* + * Return the name of the file that should cause an error for the + * given IO type. + */ +std::string fault_fname(const char* io_type); + +#endif diff --git a/otafault/include/otafault/ota_io.h b/otafault/include/otafault/ota_io.h new file mode 100644 index 000000000..45e481a62 --- /dev/null +++ b/otafault/include/otafault/ota_io.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Provide a series of proxy functions for basic file accessors. + * The behavior of these functions can be changed to return different + * errors under a variety of conditions. + */ + +#ifndef _UPDATER_OTA_IO_H_ +#define _UPDATER_OTA_IO_H_ + +#include +#include +#include // mode_t + +#include + +#include + +#define OTAIO_CACHE_FNAME "/cache/saved.file" + +void ota_set_fault_files(); + +int ota_open(const char* path, int oflags); + +int ota_open(const char* path, int oflags, mode_t mode); + +FILE* ota_fopen(const char* filename, const char* mode); + +size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream); + +ssize_t ota_read(int fd, void* buf, size_t nbyte); + +size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream); + +ssize_t ota_write(int fd, const void* buf, size_t nbyte); + +int ota_fsync(int fd); + +struct OtaCloser { + static void Close(int); +}; + +using unique_fd = android::base::unique_fd_impl; + +int ota_close(unique_fd& fd); + +struct OtaFcloser { + void operator()(FILE*) const; +}; + +using unique_file = std::unique_ptr; + +int ota_fclose(unique_file& fh); + +#endif diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp index a82a7ee59..1308973a5 100644 --- a/otafault/ota_io.cpp +++ b/otafault/ota_io.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ota_io.h" +#include "otafault/ota_io.h" #include #include @@ -29,7 +29,7 @@ #include -#include "config.h" +#include "otafault/config.h" static std::mutex filename_mutex; static std::map filename_cache GUARDED_BY(filename_mutex); diff --git a/otafault/ota_io.h b/otafault/ota_io.h deleted file mode 100644 index 45e481a62..000000000 --- a/otafault/ota_io.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Provide a series of proxy functions for basic file accessors. - * The behavior of these functions can be changed to return different - * errors under a variety of conditions. - */ - -#ifndef _UPDATER_OTA_IO_H_ -#define _UPDATER_OTA_IO_H_ - -#include -#include -#include // mode_t - -#include - -#include - -#define OTAIO_CACHE_FNAME "/cache/saved.file" - -void ota_set_fault_files(); - -int ota_open(const char* path, int oflags); - -int ota_open(const char* path, int oflags, mode_t mode); - -FILE* ota_fopen(const char* filename, const char* mode); - -size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream); - -ssize_t ota_read(int fd, void* buf, size_t nbyte); - -size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream); - -ssize_t ota_write(int fd, const void* buf, size_t nbyte); - -int ota_fsync(int fd); - -struct OtaCloser { - static void Close(int); -}; - -using unique_fd = android::base::unique_fd_impl; - -int ota_close(unique_fd& fd); - -struct OtaFcloser { - void operator()(FILE*) const; -}; - -using unique_file = std::unique_ptr; - -int ota_fclose(unique_file& fh); - -#endif diff --git a/otafault/test.cpp b/otafault/test.cpp index 60c40e099..63e2445af 100644 --- a/otafault/test.cpp +++ b/otafault/test.cpp @@ -20,7 +20,7 @@ #include #include -#include "ota_io.h" +#include "otafault/ota_io.h" int main(int /* argc */, char** /* argv */) { int fd = open("testdata/test.file", O_RDWR); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index fe21dd0eb..696cddf41 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -51,7 +51,7 @@ #include "edify/expr.h" #include "error_code.h" -#include "ota_io.h" +#include "otafault/ota_io.h" #include "print_sha1.h" #include "rangeset.h" #include "updater/install.h" diff --git a/updater/install.cpp b/updater/install.cpp index 8e54c2e75..fc085d5aa 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -59,7 +59,7 @@ #include "edify/expr.h" #include "error_code.h" #include "mounts.h" -#include "ota_io.h" +#include "otafault/ota_io.h" #include "otautil/DirUtil.h" #include "print_sha1.h" #include "tune2fs.h" diff --git a/updater/updater.cpp b/updater/updater.cpp index 1d8fa8e92..e10174f71 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -30,8 +30,8 @@ #include #include -#include "config.h" #include "edify/expr.h" +#include "otafault/config.h" #include "otautil/DirUtil.h" #include "otautil/SysUtil.h" #include "updater/blockimg.h" -- cgit v1.2.3 From ad774b297010d23911dfc75bb626cf74e374f731 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 29 Sep 2017 10:39:08 -0700 Subject: roots: Remove #include "common.h". And add the missing include of (e.g. for strcmp(3)). Minor update to the arg of fs_mgr_get_entry_for_mount_point(), which now accepts std::string. Test: mmma bootable/recovery Change-Id: I9cb8c31fe71b5a053f4d84bf1aba00e96c02ed03 --- roots.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roots.cpp b/roots.cpp index 7d7d1bdc7..02c1ecb7c 100644 --- a/roots.cpp +++ b/roots.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,6 @@ #include #include -#include "common.h" #include "mounts.h" static struct fstab* fstab = nullptr; @@ -77,7 +77,7 @@ Volume* volume_for_path(const char* path) { if (path == nullptr || path[0] == '\0') return nullptr; std::string str(path); while (true) { - Volume* result = fs_mgr_get_entry_for_mount_point(fstab, str.c_str()); + Volume* result = fs_mgr_get_entry_for_mount_point(fstab, str); if (result != nullptr || str == "/") { return result; } -- cgit v1.2.3 From a26e32d2a366c8319944b2ac887a175a870f034d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 28 Sep 2017 18:46:19 -0700 Subject: otafault: Move to soong. Test: mmma bootable/recovery Change-Id: I5f2520ea457ba66743aa3aa1d5b3b488a93084a3 --- Android.bp | 1 + Android.mk | 1 - otafault/Android.bp | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ otafault/Android.mk | 53 ---------------------------------------------- 4 files changed, 61 insertions(+), 54 deletions(-) create mode 100644 otafault/Android.bp delete mode 100644 otafault/Android.mk diff --git a/Android.bp b/Android.bp index 49438ad9e..99ca3a45c 100644 --- a/Android.bp +++ b/Android.bp @@ -1,4 +1,5 @@ subdirs = [ "bootloader_message", + "otafault", "otautil", ] diff --git a/Android.mk b/Android.mk index 801141249..c55771fbe 100644 --- a/Android.mk +++ b/Android.mk @@ -263,7 +263,6 @@ include \ $(LOCAL_PATH)/edify/Android.mk \ $(LOCAL_PATH)/minadbd/Android.mk \ $(LOCAL_PATH)/minui/Android.mk \ - $(LOCAL_PATH)/otafault/Android.mk \ $(LOCAL_PATH)/tests/Android.mk \ $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/uncrypt/Android.mk \ diff --git a/otafault/Android.bp b/otafault/Android.bp new file mode 100644 index 000000000..91a5d9a54 --- /dev/null +++ b/otafault/Android.bp @@ -0,0 +1,60 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_static { + name: "libotafault", + + srcs: [ + "config.cpp", + "ota_io.cpp", + ], + + static_libs: [ + "libbase", + "liblog", + "libziparchive", + ], + + export_include_dirs: [ + "include", + ], + + cflags: [ + "-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS", + "-Wall", + "-Werror", + "-Wthread-safety", + "-Wthread-safety-negative", + ], +} + +cc_test { + name: "otafault_test", + + srcs: ["test.cpp"], + + cflags: [ + "-Wall", + "-Werror", + ], + + static_executable: true, + + static_libs: [ + "libotafault", + "libziparchive", + "libbase", + "liblog", + ], +} diff --git a/otafault/Android.mk b/otafault/Android.mk deleted file mode 100644 index 5ec8a192a..000000000 --- a/otafault/Android.mk +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2015 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific languae governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -otafault_static_libs := \ - libziparchive \ - libbase \ - liblog - -LOCAL_CFLAGS := \ - -Wall \ - -Werror \ - -Wthread-safety \ - -Wthread-safety-negative \ - -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS - -LOCAL_SRC_FILES := config.cpp ota_io.cpp -LOCAL_MODULE_TAGS := eng -LOCAL_MODULE := libotafault -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include -LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) - -include $(BUILD_STATIC_LIBRARY) - -# otafault_test (static executable) -# =============================== -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := test.cpp -LOCAL_MODULE_TAGS := tests -LOCAL_MODULE := otafault_test -LOCAL_STATIC_LIBRARIES := \ - libotafault \ - $(otafault_static_libs) -LOCAL_CFLAGS := -Wall -Werror -LOCAL_FORCE_STATIC_EXECUTABLE := true - -include $(BUILD_EXECUTABLE) -- cgit v1.2.3 From eee1e7656f73aae7c6f7bd2997403c978ef3986e Mon Sep 17 00:00:00 2001 From: Luke Song Date: Fri, 29 Sep 2017 14:19:23 -0700 Subject: screen_ui: stage marker positioning Small fix to position stage markers above screen margin. Bug: 65556996 Test: Viewed graphics test Change-Id: Idb4a56b06b8ec4e6653b3d3f3a446fa491f5ccb7 --- screen_ui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index bc5c5c31f..166d7b4cf 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -165,7 +165,7 @@ void ScreenRecoveryUI::draw_background_locked() { int stage_height = gr_get_height(stageMarkerEmpty); int stage_width = gr_get_width(stageMarkerEmpty); int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; - int y = gr_fb_height() - stage_height; + int y = gr_fb_height() - stage_height - kMarginHeight; for (int i = 0; i < max_stage; ++i) { GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty; gr_blit(stage_surface, 0, 0, stage_width, stage_height, x, y); -- cgit v1.2.3 From 3e18f2bf4004e9df2f7fcee0d2035552404ff715 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 29 Sep 2017 17:11:13 -0700 Subject: roots: Fix an issue with volume_for_path(). The earlier commit in 2dfc1a38982c4052bb32bc7fc06edeadf3908fb9 unintentionally changed the behavior. It gives a different result when looking up non-existent mount points (e.g. /cache on marlin). The logic behind volume_for_path("/xyz") is unclear: - It's fine to return non-null value if it's called by ensure_path_mounted() before accessing that file "/xyz". (Just based on the function name, we're not actually having this case.) - It should return nullptr if the caller is interested in the existence of that particular mount point "/xyz". This CL renames the function to volume_for_mount_point(), which does an exact match by querying the given mount point from libfs_mgr. The former volume_for_path() has been moved down to function scope for serving ensure_path_mounted() only. Test: Build and boot into recovery on bullhead and marlin respectively. 'View recovery logs'. Test: 'Mount /system' Test: 'Apply update from ADB' Change-Id: I1a16390f57540cae08a2b8f3d439d17886975217 --- install.cpp | 2 +- recovery.cpp | 2 +- roots.cpp | 6 +++++- roots.h | 6 ++++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/install.cpp b/install.cpp index 507161c2e..74d1a68b3 100644 --- a/install.cpp +++ b/install.cpp @@ -653,7 +653,7 @@ int install_package(const std::string& path, bool* wipe_cache, const std::string std::chrono::duration duration = std::chrono::system_clock::now() - start; int time_total = static_cast(duration.count()); - bool has_cache = volume_for_path("/cache") != nullptr; + bool has_cache = volume_for_mount_point("/cache") != nullptr; // Skip logging the uncrypt_status on devices without /cache. if (has_cache) { static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status"; diff --git a/recovery.cpp b/recovery.cpp index 076b4492e..4dc5b540e 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1396,7 +1396,7 @@ int main(int argc, char **argv) { printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table(); - has_cache = volume_for_path(CACHE_ROOT) != nullptr; + has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr; std::vector args = get_args(argc, argv); std::vector args_to_parse(args.size()); diff --git a/roots.cpp b/roots.cpp index 02c1ecb7c..c0348d715 100644 --- a/roots.cpp +++ b/roots.cpp @@ -69,11 +69,15 @@ void load_volume_table() { printf("\n"); } +Volume* volume_for_mount_point(const std::string& mount_point) { + return fs_mgr_get_entry_for_mount_point(fstab, mount_point); +} + // Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match // only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log", // "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the // first match or nullptr. -Volume* volume_for_path(const char* path) { +static Volume* volume_for_path(const char* path) { if (path == nullptr || path[0] == '\0') return nullptr; std::string str(path); while (true) { diff --git a/roots.h b/roots.h index 542f03b9c..46bb77e02 100644 --- a/roots.h +++ b/roots.h @@ -17,13 +17,15 @@ #ifndef RECOVERY_ROOTS_H_ #define RECOVERY_ROOTS_H_ +#include + typedef struct fstab_rec Volume; // Load and parse volume data from /etc/recovery.fstab. void load_volume_table(); -// Return the Volume* record for this path (or NULL). -Volume* volume_for_path(const char* path); +// Return the Volume* record for this mount point (or nullptr). +Volume* volume_for_mount_point(const std::string& mount_point); // Make sure that the volume 'path' is on is mounted. Returns 0 on // success (volume is mounted). -- cgit v1.2.3 From 5aa77ca73d6c4ec70663f6c07c88b3eea45ef91e Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Mon, 2 Oct 2017 11:44:52 -0700 Subject: Use -Werror in bootable/recovery * Move -Werror from cppflags to cflags. Bug: 66996870 Test: build with WITH_TIDY=1 Change-Id: I59147fe24d8b48a0403c67ba07d4d384b8e9c5a3 --- bootloader_message/Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index 456b04c33..c81c67bdb 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -17,7 +17,7 @@ cc_library_static { name: "libbootloader_message", srcs: ["bootloader_message.cpp"], - cppflags: [ + cflags: [ "-Wall", "-Werror", ], -- cgit v1.2.3 From cfe53c2c018eaac7991faa98b144543c8fcd174d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 3 Oct 2017 14:37:21 -0700 Subject: otautil: Export headers. Test: mmma bootable/recovery Change-Id: Ic01b68e2a394d578fc9fc09da2dabe9061b98122 --- Android.mk | 1 + otautil/Android.bp | 4 +++ otautil/DirUtil.cpp | 2 +- otautil/DirUtil.h | 39 -------------------------- otautil/SysUtil.cpp | 2 +- otautil/SysUtil.h | 53 ----------------------------------- otautil/ThermalUtil.cpp | 4 +-- otautil/ThermalUtil.h | 24 ---------------- otautil/include/otautil/DirUtil.h | 39 ++++++++++++++++++++++++++ otautil/include/otautil/SysUtil.h | 53 +++++++++++++++++++++++++++++++++++ otautil/include/otautil/ThermalUtil.h | 24 ++++++++++++++++ 11 files changed, 125 insertions(+), 120 deletions(-) delete mode 100644 otautil/DirUtil.h delete mode 100644 otautil/SysUtil.h delete mode 100644 otautil/ThermalUtil.h create mode 100644 otautil/include/otautil/DirUtil.h create mode 100644 otautil/include/otautil/SysUtil.h create mode 100644 otautil/include/otautil/ThermalUtil.h diff --git a/Android.mk b/Android.mk index c55771fbe..ac72bac01 100644 --- a/Android.mk +++ b/Android.mk @@ -56,6 +56,7 @@ endif LOCAL_MODULE := librecovery LOCAL_STATIC_LIBRARIES := \ libminui \ + libotautil \ libvintf_recovery \ libcrypto_utils \ libcrypto \ diff --git a/otautil/Android.bp b/otautil/Android.bp index a2eaa0402..9cde7baa7 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -30,4 +30,8 @@ cc_library_static { "-Werror", "-Wall", ], + + export_include_dirs: [ + "include", + ], } diff --git a/otautil/DirUtil.cpp b/otautil/DirUtil.cpp index fffc82219..61c832813 100644 --- a/otautil/DirUtil.cpp +++ b/otautil/DirUtil.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "DirUtil.h" +#include "otautil/DirUtil.h" #include #include diff --git a/otautil/DirUtil.h b/otautil/DirUtil.h deleted file mode 100644 index 85d6c16d1..000000000 --- a/otautil/DirUtil.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OTAUTIL_DIRUTIL_H_ -#define OTAUTIL_DIRUTIL_H_ - -#include // mode_t - -#include - -struct selabel_handle; - -// Like "mkdir -p", try to guarantee that all directories specified in path are present, creating as -// many directories as necessary. The specified mode is passed to all mkdir calls; no modifications -// are made to umask. -// -// If strip_filename is set, everything after the final '/' is stripped before creating the -// directory -// hierarchy. -// -// Returns 0 on success; returns -1 (and sets errno) on failure (usually if some element of path is -// not a directory). -int mkdir_recursively(const std::string& path, mode_t mode, bool strip_filename, - const struct selabel_handle* sehnd); - -#endif // OTAUTIL_DIRUTIL_H_ diff --git a/otautil/SysUtil.cpp b/otautil/SysUtil.cpp index dfa215073..d54a824d2 100644 --- a/otautil/SysUtil.cpp +++ b/otautil/SysUtil.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "SysUtil.h" +#include "otautil/SysUtil.h" #include #include // SIZE_MAX diff --git a/otautil/SysUtil.h b/otautil/SysUtil.h deleted file mode 100644 index 52f6d20a7..000000000 --- a/otautil/SysUtil.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _OTAUTIL_SYSUTIL -#define _OTAUTIL_SYSUTIL - -#include - -#include -#include - -/* - * Use this to keep track of mapped segments. - */ -class MemMapping { - public: - ~MemMapping(); - // Map a file into a private, read-only memory segment. If 'filename' begins with an '@' - // character, it is a map of blocks to be mapped, otherwise it is treated as an ordinary file. - bool MapFile(const std::string& filename); - size_t ranges() const { - return ranges_.size(); - }; - - unsigned char* addr; // start of data - size_t length; // length of data - - private: - struct MappedRange { - void* addr; - size_t length; - }; - - bool MapBlockFile(const std::string& filename); - bool MapFD(int fd); - - std::vector ranges_; -}; - -#endif // _OTAUTIL_SYSUTIL diff --git a/otautil/ThermalUtil.cpp b/otautil/ThermalUtil.cpp index 13d36432a..5d9bd45c0 100644 --- a/otautil/ThermalUtil.cpp +++ b/otautil/ThermalUtil.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ThermalUtil.h" +#include "otautil/ThermalUtil.h" #include #include @@ -77,4 +77,4 @@ int GetMaxValueFromThermalZone() { } LOG(INFO) << "current maximum temperature: " << max_temperature; return max_temperature; -} \ No newline at end of file +} diff --git a/otautil/ThermalUtil.h b/otautil/ThermalUtil.h deleted file mode 100644 index 43ab55940..000000000 --- a/otautil/ThermalUtil.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef OTAUTIL_THERMALUTIL_H -#define OTAUTIL_THERMALUTIL_H - -// We can find the temperature reported by all sensors in /sys/class/thermal/thermal_zone*/temp. -// Their values are in millidegree Celsius; and we will log the maximum one. -int GetMaxValueFromThermalZone(); - -#endif // OTAUTIL_THERMALUTIL_H diff --git a/otautil/include/otautil/DirUtil.h b/otautil/include/otautil/DirUtil.h new file mode 100644 index 000000000..85d6c16d1 --- /dev/null +++ b/otautil/include/otautil/DirUtil.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OTAUTIL_DIRUTIL_H_ +#define OTAUTIL_DIRUTIL_H_ + +#include // mode_t + +#include + +struct selabel_handle; + +// Like "mkdir -p", try to guarantee that all directories specified in path are present, creating as +// many directories as necessary. The specified mode is passed to all mkdir calls; no modifications +// are made to umask. +// +// If strip_filename is set, everything after the final '/' is stripped before creating the +// directory +// hierarchy. +// +// Returns 0 on success; returns -1 (and sets errno) on failure (usually if some element of path is +// not a directory). +int mkdir_recursively(const std::string& path, mode_t mode, bool strip_filename, + const struct selabel_handle* sehnd); + +#endif // OTAUTIL_DIRUTIL_H_ diff --git a/otautil/include/otautil/SysUtil.h b/otautil/include/otautil/SysUtil.h new file mode 100644 index 000000000..52f6d20a7 --- /dev/null +++ b/otautil/include/otautil/SysUtil.h @@ -0,0 +1,53 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _OTAUTIL_SYSUTIL +#define _OTAUTIL_SYSUTIL + +#include + +#include +#include + +/* + * Use this to keep track of mapped segments. + */ +class MemMapping { + public: + ~MemMapping(); + // Map a file into a private, read-only memory segment. If 'filename' begins with an '@' + // character, it is a map of blocks to be mapped, otherwise it is treated as an ordinary file. + bool MapFile(const std::string& filename); + size_t ranges() const { + return ranges_.size(); + }; + + unsigned char* addr; // start of data + size_t length; // length of data + + private: + struct MappedRange { + void* addr; + size_t length; + }; + + bool MapBlockFile(const std::string& filename); + bool MapFD(int fd); + + std::vector ranges_; +}; + +#endif // _OTAUTIL_SYSUTIL diff --git a/otautil/include/otautil/ThermalUtil.h b/otautil/include/otautil/ThermalUtil.h new file mode 100644 index 000000000..43ab55940 --- /dev/null +++ b/otautil/include/otautil/ThermalUtil.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OTAUTIL_THERMALUTIL_H +#define OTAUTIL_THERMALUTIL_H + +// We can find the temperature reported by all sensors in /sys/class/thermal/thermal_zone*/temp. +// Their values are in millidegree Celsius; and we will log the maximum one. +int GetMaxValueFromThermalZone(); + +#endif // OTAUTIL_THERMALUTIL_H -- cgit v1.2.3 From 6e4a9ae51acca00134d7771b53571b4d6bc185b3 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 3 Oct 2017 14:49:32 -0700 Subject: edify: Remove edify_parser. It used to be containing some unit tests for basic edify syntax, which has been moved into recovery_component_test (commit d770d2e7afd8a909156447c2b79d8739228279d7). The edify_parser host tool supports edify built-in functions only, but doesn't recognize the ones defined in updater. This makes it much less useful to do any real analyzing work. Test: mmma bootable/recovery Change-Id: I3c12f5402d2d6698e0ef5ac6c2e7804c0fbba78a --- edify/Android.mk | 20 ------------- edify/edify_parser.cpp | 79 -------------------------------------------------- 2 files changed, 99 deletions(-) delete mode 100644 edify/edify_parser.cpp diff --git a/edify/Android.mk b/edify/Android.mk index baf4dd21d..cec65f42a 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -19,26 +19,6 @@ edify_src_files := \ parser.yy \ expr.cpp -# -# Build the host-side command line tool (host executable) -# -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - $(edify_src_files) \ - edify_parser.cpp - -LOCAL_CFLAGS := -Wall -Werror -LOCAL_CPPFLAGS := -g -O0 -LOCAL_MODULE := edify_parser -LOCAL_YACCFLAGS := -v -LOCAL_CPPFLAGS += -Wno-unused-parameter -LOCAL_CPPFLAGS += -Wno-deprecated-register -LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -LOCAL_STATIC_LIBRARIES += libbase - -include $(BUILD_HOST_EXECUTABLE) - # # Build the device-side library (static library) # diff --git a/edify/edify_parser.cpp b/edify/edify_parser.cpp deleted file mode 100644 index f1b56284c..000000000 --- a/edify/edify_parser.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -/** - * This is a host-side tool for validating a given edify script file. - * - * We used to have edify test cases here, which have been moved to - * tests/component/edify_test.cpp. - * - * Caveat: It doesn't recognize functions defined through updater, which - * makes the tool less useful. We should either extend the tool or remove it. - */ - -#include -#include - -#include -#include - -#include - -#include "expr.h" - -static void ExprDump(int depth, const std::unique_ptr& n, const std::string& script) { - printf("%*s", depth*2, ""); - printf("%s %p (%d-%d) \"%s\"\n", - n->name.c_str(), n->fn, n->start, n->end, - script.substr(n->start, n->end - n->start).c_str()); - for (size_t i = 0; i < n->argv.size(); ++i) { - ExprDump(depth+1, n->argv[i], script); - } -} - -int main(int argc, char** argv) { - RegisterBuiltins(); - - if (argc != 2) { - printf("Usage: %s \n", argv[0]); - return 1; - } - - std::string buffer; - if (!android::base::ReadFileToString(argv[1], &buffer)) { - printf("%s: failed to read %s: %s\n", argv[0], argv[1], strerror(errno)); - return 1; - } - - std::unique_ptr root; - int error_count = 0; - int error = parse_string(buffer.data(), &root, &error_count); - printf("parse returned %d; %d errors encountered\n", error, error_count); - if (error == 0 || error_count > 0) { - - ExprDump(0, root, buffer); - - State state(buffer, nullptr); - std::string result; - if (!Evaluate(&state, root, &result)) { - printf("result was NULL, message is: %s\n", - (state.errmsg.empty() ? "(NULL)" : state.errmsg.c_str())); - } else { - printf("result is [%s]\n", result.c_str()); - } - } - return 0; -} -- cgit v1.2.3 From 623fe7e701d5d0fb17082d1ced14498af1b44e5b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 2 Oct 2017 16:28:06 -0700 Subject: Move error_code.h into otautil. This way it stops requiring relative path ".." in LOCAL_C_INCLUDES (uncrypt and edify). Soong doesn't accept non-local ".." in "local_include_dirs". Test: mmma bootable/recovery Change-Id: Ia4649789cef2aaeb2785483660e9ea5a8b389c62 --- applypatch/Android.mk | 5 +++ edify/Android.mk | 5 ++- edify/expr.h | 2 +- error_code.h | 78 ------------------------------------ install.cpp | 2 +- otautil/Android.bp | 2 + otautil/include/otautil/error_code.h | 78 ++++++++++++++++++++++++++++++++++++ recovery.cpp | 2 +- tests/component/updater_test.cpp | 2 +- uncrypt/Android.mk | 2 +- uncrypt/uncrypt.cpp | 2 +- updater/blockimg.cpp | 2 +- updater/install.cpp | 2 +- 13 files changed, 96 insertions(+), 88 deletions(-) delete mode 100644 error_code.h create mode 100644 otautil/include/otautil/error_code.h diff --git a/applypatch/Android.mk b/applypatch/Android.mk index f5dda2bc4..f02957e7e 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -30,6 +30,7 @@ LOCAL_C_INCLUDES := \ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libotafault \ + libotautil \ libbase \ libcrypto \ libbspatch \ @@ -53,6 +54,7 @@ LOCAL_C_INCLUDES := \ bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ + libotautil \ libcrypto \ libbspatch \ libbase \ @@ -77,6 +79,7 @@ LOCAL_C_INCLUDES := \ bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ + libotautil \ libcrypto \ libbspatch \ libbase \ @@ -99,6 +102,7 @@ LOCAL_STATIC_LIBRARIES := \ libapplypatch \ libbase \ libedify \ + libotautil \ libcrypto LOCAL_CFLAGS := -Wall -Werror include $(BUILD_STATIC_LIBRARY) @@ -114,6 +118,7 @@ LOCAL_STATIC_LIBRARIES := \ libapplypatch \ libedify \ libotafault \ + libotautil \ libbspatch \ libbase \ libziparchive \ diff --git a/edify/Android.mk b/edify/Android.mk index cec65f42a..db7b5b6b5 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -30,7 +30,8 @@ LOCAL_CFLAGS := -Wall -Werror LOCAL_CPPFLAGS := -Wno-unused-parameter LOCAL_CPPFLAGS += -Wno-deprecated-register LOCAL_MODULE := libedify -LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -LOCAL_STATIC_LIBRARIES += libbase +LOCAL_STATIC_LIBRARIES += \ + libotautil \ + libbase include $(BUILD_STATIC_LIBRARY) diff --git a/edify/expr.h b/edify/expr.h index 4838d20c0..f2a4d6dcf 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -23,7 +23,7 @@ #include #include -#include "error_code.h" +#include "otautil/error_code.h" struct State { State(const std::string& script, void* cookie); diff --git a/error_code.h b/error_code.h deleted file mode 100644 index 4e3032bc9..000000000 --- a/error_code.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _ERROR_CODE_H_ -#define _ERROR_CODE_H_ - -enum ErrorCode { - kNoError = -1, - kLowBattery = 20, - kZipVerificationFailure, - kZipOpenFailure, - kBootreasonInBlacklist, - kPackageCompatibilityFailure, - kScriptExecutionFailure, - kMapFileFailure, - kForkUpdateBinaryFailure, - kUpdateBinaryCommandFailure, -}; - -enum CauseCode { - kNoCause = -1, - kArgsParsingFailure = 100, - kStashCreationFailure, - kFileOpenFailure, - kLseekFailure, - kFreadFailure, - kFwriteFailure, - kFsyncFailure, - kLibfecFailure, - kFileGetPropFailure, - kFileRenameFailure, - kSymlinkFailure, - kSetMetadataFailure, - kTune2FsFailure, - kRebootFailure, - kPackageExtractFileFailure, - kPatchApplicationFailure, - kVendorFailure = 200 -}; - -enum UncryptErrorCode { - kUncryptNoError = -1, - kUncryptErrorPlaceholder = 50, - kUncryptTimeoutError = 100, - kUncryptFileRemoveError, - kUncryptFileOpenError, - kUncryptSocketOpenError, - kUncryptSocketWriteError, - kUncryptSocketListenError, - kUncryptSocketAcceptError, - kUncryptFstabReadError, - kUncryptFileStatError, - kUncryptBlockOpenError, - kUncryptIoctlError, - kUncryptReadError, - kUncryptWriteError, - kUncryptFileSyncError, - kUncryptFileCloseError, - kUncryptFileRenameError, - kUncryptPackageMissingError, - kUncryptRealpathFindError, - kUncryptBlockDeviceFindError, -}; - -#endif // _ERROR_CODE_H_ diff --git a/install.cpp b/install.cpp index 74d1a68b3..d05893171 100644 --- a/install.cpp +++ b/install.cpp @@ -49,9 +49,9 @@ #include #include "common.h" -#include "error_code.h" #include "otautil/SysUtil.h" #include "otautil/ThermalUtil.h" +#include "otautil/error_code.h" #include "private/install.h" #include "roots.h" #include "ui.h" diff --git a/otautil/Android.bp b/otautil/Android.bp index 9cde7baa7..5905ba649 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -15,6 +15,8 @@ cc_library_static { name: "libotautil", + host_supported: true, + srcs: [ "SysUtil.cpp", "DirUtil.cpp", diff --git a/otautil/include/otautil/error_code.h b/otautil/include/otautil/error_code.h new file mode 100644 index 000000000..943c7622d --- /dev/null +++ b/otautil/include/otautil/error_code.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ERROR_CODE_H_ +#define _ERROR_CODE_H_ + +enum ErrorCode { + kNoError = -1, + kLowBattery = 20, + kZipVerificationFailure, + kZipOpenFailure, + kBootreasonInBlacklist, + kPackageCompatibilityFailure, + kScriptExecutionFailure, + kMapFileFailure, + kForkUpdateBinaryFailure, + kUpdateBinaryCommandFailure, +}; + +enum CauseCode { + kNoCause = -1, + kArgsParsingFailure = 100, + kStashCreationFailure, + kFileOpenFailure, + kLseekFailure, + kFreadFailure, + kFwriteFailure, + kFsyncFailure, + kLibfecFailure, + kFileGetPropFailure, + kFileRenameFailure, + kSymlinkFailure, + kSetMetadataFailure, + kTune2FsFailure, + kRebootFailure, + kPackageExtractFileFailure, + kPatchApplicationFailure, + kVendorFailure = 200 +}; + +enum UncryptErrorCode { + kUncryptNoError = -1, + kUncryptErrorPlaceholder = 50, + kUncryptTimeoutError = 100, + kUncryptFileRemoveError, + kUncryptFileOpenError, + kUncryptSocketOpenError, + kUncryptSocketWriteError, + kUncryptSocketListenError, + kUncryptSocketAcceptError, + kUncryptFstabReadError, + kUncryptFileStatError, + kUncryptBlockOpenError, + kUncryptIoctlError, + kUncryptReadError, + kUncryptWriteError, + kUncryptFileSyncError, + kUncryptFileCloseError, + kUncryptFileRenameError, + kUncryptPackageMissingError, + kUncryptRealpathFindError, + kUncryptBlockDeviceFindError, +}; + +#endif // _ERROR_CODE_H_ diff --git a/recovery.cpp b/recovery.cpp index 4dc5b540e..243ee4af3 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -61,13 +61,13 @@ #include "adb_install.h" #include "common.h" #include "device.h" -#include "error_code.h" #include "fuse_sdcard_provider.h" #include "fuse_sideload.h" #include "install.h" #include "minadbd/minadbd.h" #include "minui/minui.h" #include "otautil/DirUtil.h" +#include "otautil/error_code.h" #include "roots.h" #include "rotate_logs.h" #include "screen_ui.h" diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 2a0575a31..e35870dc7 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -39,8 +39,8 @@ #include "common/test_constants.h" #include "edify/expr.h" -#include "error_code.h" #include "otautil/SysUtil.h" +#include "otautil/error_code.h" #include "print_sha1.h" #include "updater/blockimg.h" #include "updater/install.h" diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index a3b0ca98d..601f9276e 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -17,10 +17,10 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := uncrypt.cpp -LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. LOCAL_MODULE := uncrypt LOCAL_STATIC_LIBRARIES := \ libbootloader_message \ + libotautil \ libbase \ liblog \ libfs_mgr \ diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 7a2ccbc7c..645faadbf 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -116,7 +116,7 @@ #include #include -#include "error_code.h" +#include "otautil/error_code.h" static constexpr int WINDOW_SIZE = 5; static constexpr int FIBMAP_RETRY_LIMIT = 3; diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 696cddf41..0f8364441 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -50,8 +50,8 @@ #include #include "edify/expr.h" -#include "error_code.h" #include "otafault/ota_io.h" +#include "otautil/error_code.h" #include "print_sha1.h" #include "rangeset.h" #include "updater/install.h" diff --git a/updater/install.cpp b/updater/install.cpp index fc085d5aa..01210f51c 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -57,10 +57,10 @@ #include #include "edify/expr.h" -#include "error_code.h" #include "mounts.h" #include "otafault/ota_io.h" #include "otautil/DirUtil.h" +#include "otautil/error_code.h" #include "print_sha1.h" #include "tune2fs.h" #include "updater/updater.h" -- cgit v1.2.3 From 26436d6d6010d5323349af7e119ff8f34f85c40c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 5 Oct 2017 17:16:31 +0000 Subject: Revert "Move error_code.h into otautil." This reverts commit 623fe7e701d5d0fb17082d1ced14498af1b44e5b. Reason for revert: Need to address device-specific modules. Change-Id: Ib7a4191e7f193dfff49b02d3de76dda856800251 --- applypatch/Android.mk | 5 --- edify/Android.mk | 5 +-- edify/expr.h | 2 +- error_code.h | 78 ++++++++++++++++++++++++++++++++++++ install.cpp | 2 +- otautil/Android.bp | 2 - otautil/include/otautil/error_code.h | 78 ------------------------------------ recovery.cpp | 2 +- tests/component/updater_test.cpp | 2 +- uncrypt/Android.mk | 2 +- uncrypt/uncrypt.cpp | 2 +- updater/blockimg.cpp | 2 +- updater/install.cpp | 2 +- 13 files changed, 88 insertions(+), 96 deletions(-) create mode 100644 error_code.h delete mode 100644 otautil/include/otautil/error_code.h diff --git a/applypatch/Android.mk b/applypatch/Android.mk index f02957e7e..f5dda2bc4 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -30,7 +30,6 @@ LOCAL_C_INCLUDES := \ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libotafault \ - libotautil \ libbase \ libcrypto \ libbspatch \ @@ -54,7 +53,6 @@ LOCAL_C_INCLUDES := \ bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ - libotautil \ libcrypto \ libbspatch \ libbase \ @@ -79,7 +77,6 @@ LOCAL_C_INCLUDES := \ bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ - libotautil \ libcrypto \ libbspatch \ libbase \ @@ -102,7 +99,6 @@ LOCAL_STATIC_LIBRARIES := \ libapplypatch \ libbase \ libedify \ - libotautil \ libcrypto LOCAL_CFLAGS := -Wall -Werror include $(BUILD_STATIC_LIBRARY) @@ -118,7 +114,6 @@ LOCAL_STATIC_LIBRARIES := \ libapplypatch \ libedify \ libotafault \ - libotautil \ libbspatch \ libbase \ libziparchive \ diff --git a/edify/Android.mk b/edify/Android.mk index db7b5b6b5..cec65f42a 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -30,8 +30,7 @@ LOCAL_CFLAGS := -Wall -Werror LOCAL_CPPFLAGS := -Wno-unused-parameter LOCAL_CPPFLAGS += -Wno-deprecated-register LOCAL_MODULE := libedify -LOCAL_STATIC_LIBRARIES += \ - libotautil \ - libbase +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES += libbase include $(BUILD_STATIC_LIBRARY) diff --git a/edify/expr.h b/edify/expr.h index f2a4d6dcf..4838d20c0 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -23,7 +23,7 @@ #include #include -#include "otautil/error_code.h" +#include "error_code.h" struct State { State(const std::string& script, void* cookie); diff --git a/error_code.h b/error_code.h new file mode 100644 index 000000000..4e3032bc9 --- /dev/null +++ b/error_code.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ERROR_CODE_H_ +#define _ERROR_CODE_H_ + +enum ErrorCode { + kNoError = -1, + kLowBattery = 20, + kZipVerificationFailure, + kZipOpenFailure, + kBootreasonInBlacklist, + kPackageCompatibilityFailure, + kScriptExecutionFailure, + kMapFileFailure, + kForkUpdateBinaryFailure, + kUpdateBinaryCommandFailure, +}; + +enum CauseCode { + kNoCause = -1, + kArgsParsingFailure = 100, + kStashCreationFailure, + kFileOpenFailure, + kLseekFailure, + kFreadFailure, + kFwriteFailure, + kFsyncFailure, + kLibfecFailure, + kFileGetPropFailure, + kFileRenameFailure, + kSymlinkFailure, + kSetMetadataFailure, + kTune2FsFailure, + kRebootFailure, + kPackageExtractFileFailure, + kPatchApplicationFailure, + kVendorFailure = 200 +}; + +enum UncryptErrorCode { + kUncryptNoError = -1, + kUncryptErrorPlaceholder = 50, + kUncryptTimeoutError = 100, + kUncryptFileRemoveError, + kUncryptFileOpenError, + kUncryptSocketOpenError, + kUncryptSocketWriteError, + kUncryptSocketListenError, + kUncryptSocketAcceptError, + kUncryptFstabReadError, + kUncryptFileStatError, + kUncryptBlockOpenError, + kUncryptIoctlError, + kUncryptReadError, + kUncryptWriteError, + kUncryptFileSyncError, + kUncryptFileCloseError, + kUncryptFileRenameError, + kUncryptPackageMissingError, + kUncryptRealpathFindError, + kUncryptBlockDeviceFindError, +}; + +#endif // _ERROR_CODE_H_ diff --git a/install.cpp b/install.cpp index d05893171..74d1a68b3 100644 --- a/install.cpp +++ b/install.cpp @@ -49,9 +49,9 @@ #include #include "common.h" +#include "error_code.h" #include "otautil/SysUtil.h" #include "otautil/ThermalUtil.h" -#include "otautil/error_code.h" #include "private/install.h" #include "roots.h" #include "ui.h" diff --git a/otautil/Android.bp b/otautil/Android.bp index 5905ba649..9cde7baa7 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -15,8 +15,6 @@ cc_library_static { name: "libotautil", - host_supported: true, - srcs: [ "SysUtil.cpp", "DirUtil.cpp", diff --git a/otautil/include/otautil/error_code.h b/otautil/include/otautil/error_code.h deleted file mode 100644 index 943c7622d..000000000 --- a/otautil/include/otautil/error_code.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _ERROR_CODE_H_ -#define _ERROR_CODE_H_ - -enum ErrorCode { - kNoError = -1, - kLowBattery = 20, - kZipVerificationFailure, - kZipOpenFailure, - kBootreasonInBlacklist, - kPackageCompatibilityFailure, - kScriptExecutionFailure, - kMapFileFailure, - kForkUpdateBinaryFailure, - kUpdateBinaryCommandFailure, -}; - -enum CauseCode { - kNoCause = -1, - kArgsParsingFailure = 100, - kStashCreationFailure, - kFileOpenFailure, - kLseekFailure, - kFreadFailure, - kFwriteFailure, - kFsyncFailure, - kLibfecFailure, - kFileGetPropFailure, - kFileRenameFailure, - kSymlinkFailure, - kSetMetadataFailure, - kTune2FsFailure, - kRebootFailure, - kPackageExtractFileFailure, - kPatchApplicationFailure, - kVendorFailure = 200 -}; - -enum UncryptErrorCode { - kUncryptNoError = -1, - kUncryptErrorPlaceholder = 50, - kUncryptTimeoutError = 100, - kUncryptFileRemoveError, - kUncryptFileOpenError, - kUncryptSocketOpenError, - kUncryptSocketWriteError, - kUncryptSocketListenError, - kUncryptSocketAcceptError, - kUncryptFstabReadError, - kUncryptFileStatError, - kUncryptBlockOpenError, - kUncryptIoctlError, - kUncryptReadError, - kUncryptWriteError, - kUncryptFileSyncError, - kUncryptFileCloseError, - kUncryptFileRenameError, - kUncryptPackageMissingError, - kUncryptRealpathFindError, - kUncryptBlockDeviceFindError, -}; - -#endif // _ERROR_CODE_H_ diff --git a/recovery.cpp b/recovery.cpp index 243ee4af3..4dc5b540e 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -61,13 +61,13 @@ #include "adb_install.h" #include "common.h" #include "device.h" +#include "error_code.h" #include "fuse_sdcard_provider.h" #include "fuse_sideload.h" #include "install.h" #include "minadbd/minadbd.h" #include "minui/minui.h" #include "otautil/DirUtil.h" -#include "otautil/error_code.h" #include "roots.h" #include "rotate_logs.h" #include "screen_ui.h" diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index e35870dc7..2a0575a31 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -39,8 +39,8 @@ #include "common/test_constants.h" #include "edify/expr.h" +#include "error_code.h" #include "otautil/SysUtil.h" -#include "otautil/error_code.h" #include "print_sha1.h" #include "updater/blockimg.h" #include "updater/install.h" diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 601f9276e..a3b0ca98d 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -17,10 +17,10 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := uncrypt.cpp +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. LOCAL_MODULE := uncrypt LOCAL_STATIC_LIBRARIES := \ libbootloader_message \ - libotautil \ libbase \ liblog \ libfs_mgr \ diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 645faadbf..7a2ccbc7c 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -116,7 +116,7 @@ #include #include -#include "otautil/error_code.h" +#include "error_code.h" static constexpr int WINDOW_SIZE = 5; static constexpr int FIBMAP_RETRY_LIMIT = 3; diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 0f8364441..696cddf41 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -50,8 +50,8 @@ #include #include "edify/expr.h" +#include "error_code.h" #include "otafault/ota_io.h" -#include "otautil/error_code.h" #include "print_sha1.h" #include "rangeset.h" #include "updater/install.h" diff --git a/updater/install.cpp b/updater/install.cpp index 01210f51c..fc085d5aa 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -57,10 +57,10 @@ #include #include "edify/expr.h" +#include "error_code.h" #include "mounts.h" #include "otafault/ota_io.h" #include "otautil/DirUtil.h" -#include "otautil/error_code.h" #include "print_sha1.h" #include "tune2fs.h" #include "updater/updater.h" -- cgit v1.2.3 From 92eda4db6cf6aae9fb9954c2555742ad7d1cd96a Mon Sep 17 00:00:00 2001 From: Luke Song Date: Tue, 19 Sep 2017 10:51:35 -0700 Subject: vr_ui: drawing changes Change drawing of horizontal bars. Implement image and background drawing. Bug: 65556996 Test: Viewed graphics test Change-Id: I68ddd997123607dbebf972af5a455ce8ef0c7075 --- screen_ui.cpp | 64 +++++++++++++++++++++++++++++++++++++++-------------------- screen_ui.h | 11 ++++++++++ vr_ui.cpp | 44 +++++++++++++++++++++++++++++++++------- vr_ui.h | 8 +++++++- 4 files changed, 97 insertions(+), 30 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index 166d7b4cf..c8fb5aa75 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -149,8 +149,8 @@ int ScreenRecoveryUI::GetProgressBaseline() const { int elements_sum = gr_get_height(loopFrames[0]) + PixelsFromDp(kLayouts[layout_][ICON]) + gr_get_height(installing_text) + PixelsFromDp(kLayouts[layout_][TEXT]) + gr_get_height(progressBarFill); - int bottom_gap = (gr_fb_height() - elements_sum) / 2; - return gr_fb_height() - bottom_gap - gr_get_height(progressBarFill); + int bottom_gap = (ScreenHeight() - elements_sum) / 2; + return ScreenHeight() - bottom_gap - gr_get_height(progressBarFill); } // Clear the screen and draw the currently selected background icon (if any). @@ -159,25 +159,24 @@ void ScreenRecoveryUI::draw_background_locked() { pagesIdentical = false; gr_color(0, 0, 0, 255); gr_clear(); - if (currentIcon != NONE) { if (max_stage != -1) { int stage_height = gr_get_height(stageMarkerEmpty); int stage_width = gr_get_width(stageMarkerEmpty); - int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; - int y = gr_fb_height() - stage_height - kMarginHeight; + int x = (ScreenWidth() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; + int y = ScreenHeight() - stage_height - kMarginHeight; for (int i = 0; i < max_stage; ++i) { GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty; - gr_blit(stage_surface, 0, 0, stage_width, stage_height, x, y); + DrawSurface(stage_surface, 0, 0, stage_width, stage_height, x, y); x += stage_width; } } GRSurface* text_surface = GetCurrentText(); - int text_x = (gr_fb_width() - gr_get_width(text_surface)) / 2; + int text_x = (ScreenWidth() - gr_get_width(text_surface)) / 2; int text_y = GetTextBaseline(); gr_color(255, 255, 255, 255); - gr_texticon(text_x, text_y, text_surface); + DrawTextIcon(text_x, text_y, text_surface); } } @@ -188,21 +187,21 @@ void ScreenRecoveryUI::draw_foreground_locked() { GRSurface* frame = GetCurrentFrame(); int frame_width = gr_get_width(frame); int frame_height = gr_get_height(frame); - int frame_x = (gr_fb_width() - frame_width) / 2; + int frame_x = (ScreenWidth() - frame_width) / 2; int frame_y = GetAnimationBaseline(); - gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); + DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); } if (progressBarType != EMPTY) { int width = gr_get_width(progressBarEmpty); int height = gr_get_height(progressBarEmpty); - int progress_x = (gr_fb_width() - width) / 2; + int progress_x = (ScreenWidth() - width) / 2; int progress_y = GetProgressBaseline(); // Erase behind the progress bar (in case this was a progress-only update) gr_color(0, 0, 0, 255); - gr_fill(progress_x, progress_y, width, height); + DrawFill(progress_x, progress_y, width, height); if (progressBarType == DETERMINATE) { float p = progressScopeStart + progress * progressScopeSize; @@ -211,19 +210,19 @@ void ScreenRecoveryUI::draw_foreground_locked() { if (rtl_locale_) { // Fill the progress bar from right to left. if (pos > 0) { - gr_blit(progressBarFill, width - pos, 0, pos, height, progress_x + width - pos, - progress_y); + DrawSurface(progressBarFill, width - pos, 0, pos, height, progress_x + width - pos, + progress_y); } if (pos < width - 1) { - gr_blit(progressBarEmpty, 0, 0, width - pos, height, progress_x, progress_y); + DrawSurface(progressBarEmpty, 0, 0, width - pos, height, progress_x, progress_y); } } else { // Fill the progress bar from left to right. if (pos > 0) { - gr_blit(progressBarFill, 0, 0, pos, height, progress_x, progress_y); + DrawSurface(progressBarFill, 0, 0, pos, height, progress_x, progress_y); } if (pos < width - 1) { - gr_blit(progressBarEmpty, pos, 0, width - pos, height, progress_x + pos, progress_y); + DrawSurface(progressBarEmpty, pos, 0, width - pos, height, progress_x + pos, progress_y); } } } @@ -335,8 +334,21 @@ void ScreenRecoveryUI::CheckBackgroundTextImages(const std::string& saved_locale SetLocale(saved_locale); } +int ScreenRecoveryUI::ScreenWidth() const { + return gr_fb_width(); +} + +int ScreenRecoveryUI::ScreenHeight() const { + return gr_fb_height(); +} + +void ScreenRecoveryUI::DrawSurface(GRSurface* surface, int sx, int sy, int w, int h, int dx, + int dy) const { + gr_blit(surface, sx, sy, w, h, dx, dy); +} + int ScreenRecoveryUI::DrawHorizontalRule(int y) const { - gr_fill(0, y + 4, gr_fb_width(), y + 6); + gr_fill(0, y + 4, ScreenWidth(), y + 6); return 8; } @@ -344,6 +356,14 @@ void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) con gr_fill(x, y, x + width, y + height); } +void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const { + gr_fill(x, y, w, h); +} + +void ScreenRecoveryUI::DrawTextIcon(int x, int y, GRSurface* surface) const { + gr_texticon(x, y, surface); +} + int ScreenRecoveryUI::DrawTextLine(int x, int y, const char* line, bool bold) const { gr_text(gr_sys_font(), x, y, line, bold); return char_height_ + 4; @@ -432,7 +452,7 @@ void ScreenRecoveryUI::draw_screen_locked() { if (i == menu_sel) { // Draw the highlight bar. SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); + DrawHighlightBar(0, y - 2, ScreenWidth(), char_height_ + 4); // Bold white text for the selected item. SetColor(MENU_SEL_FG); y += DrawTextLine(x, y, menu_[i].c_str(), true); @@ -449,7 +469,7 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = text_row_; size_t count = 0; - for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; + for (int ty = ScreenHeight() - kMarginHeight - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { DrawTextLine(kMarginWidth, ty, text_[row], false); --row; @@ -569,8 +589,8 @@ bool ScreenRecoveryUI::InitTextParams() { } gr_font_size(gr_sys_font(), &char_width_, &char_height_); - text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; - text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; + text_rows_ = (ScreenHeight() - kMarginHeight * 2) / char_height_; + text_cols_ = (ScreenWidth() - kMarginWidth * 2) / char_width_; return true; } diff --git a/screen_ui.h b/screen_ui.h index 3a28a09de..f05761c42 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -124,12 +124,23 @@ class ScreenRecoveryUI : public RecoveryUI { virtual int GetProgressBaseline() const; virtual int GetTextBaseline() const; + // Returns pixel width of draw buffer. + virtual int ScreenWidth() const; + // Returns pixel height of draw buffer. + virtual int ScreenHeight() const; + // Draws a highlight bar at (x, y) - (x + width, y + height). virtual void DrawHighlightBar(int x, int y, int width, int height) const; // Draws a horizontal rule at Y. Returns the offset it should be moving along Y-axis. virtual int DrawHorizontalRule(int y) const; // Draws a line of text. Returns the offset it should be moving along Y-axis. virtual int DrawTextLine(int x, int y, const char* line, bool bold) const; + // Draws surface portion (sx, sy, w, h) at screen location (dx, dy). + virtual void DrawSurface(GRSurface* surface, int sx, int sy, int w, int h, int dx, int dy) const; + // Draws rectangle at (x, y) - (x + w, y + h). + virtual void DrawFill(int x, int y, int w, int h) const; + // Draws given surface (surface->pixel_bytes = 1) as text at (x, y). + virtual void DrawTextIcon(int x, int y, GRSurface* surface) const; // Draws multiple text lines. Returns the offset it should be moving along Y-axis. int DrawTextLines(int x, int y, const char* const* lines) const; // Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines. diff --git a/vr_ui.cpp b/vr_ui.cpp index 125167268..07cc9da59 100644 --- a/vr_ui.cpp +++ b/vr_ui.cpp @@ -20,16 +20,46 @@ VrRecoveryUI::VrRecoveryUI() : kStereoOffset(RECOVERY_UI_VR_STEREO_OFFSET) {} -bool VrRecoveryUI::InitTextParams() { - if (!ScreenRecoveryUI::InitTextParams()) return false; - int mid_divide = gr_fb_width() / 2; - text_cols_ = (mid_divide - kMarginWidth - kStereoOffset) / char_width_; - return true; +int VrRecoveryUI::ScreenWidth() const { + return gr_fb_width() / 2; +} + +int VrRecoveryUI::ScreenHeight() const { + return gr_fb_height(); +} + +void VrRecoveryUI::DrawSurface(GRSurface* surface, int sx, int sy, int w, int h, int dx, + int dy) const { + gr_blit(surface, sx, sy, w, h, dx + kStereoOffset, dy); + gr_blit(surface, sx, sy, w, h, dx - kStereoOffset + ScreenWidth(), dy); +} + +void VrRecoveryUI::DrawTextIcon(int x, int y, GRSurface* surface) const { + gr_texticon(x + kStereoOffset, y, surface); + gr_texticon(x - kStereoOffset + ScreenWidth(), y, surface); } int VrRecoveryUI::DrawTextLine(int x, int y, const char* line, bool bold) const { - int mid_divide = gr_fb_width() / 2; gr_text(gr_sys_font(), x + kStereoOffset, y, line, bold); - gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, y, line, bold); + gr_text(gr_sys_font(), x - kStereoOffset + ScreenWidth(), y, line, bold); return char_height_ + 4; } + +int VrRecoveryUI::DrawHorizontalRule(int y) const { + y += 4; + gr_fill(kMarginWidth + kStereoOffset, y, ScreenWidth() - kMarginWidth + kStereoOffset, y + 2); + gr_fill(ScreenWidth() + kMarginWidth - kStereoOffset, y, + gr_fb_width() - kMarginWidth - kStereoOffset, y + 2); + return y + 4; +} + +void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { + gr_fill(kMarginWidth + kStereoOffset, y, ScreenWidth() - kMarginWidth + kStereoOffset, y + height); + gr_fill(ScreenWidth() + kMarginWidth - kStereoOffset, y, + gr_fb_width() - kMarginWidth - kStereoOffset, y + height); +} + +void VrRecoveryUI::DrawFill(int x, int y, int w, int h) const { + gr_fill(x + kStereoOffset, y, w, h); + gr_fill(x - kStereoOffset + ScreenWidth(), y, w, h); +} diff --git a/vr_ui.h b/vr_ui.h index d996c145f..eeb458912 100644 --- a/vr_ui.h +++ b/vr_ui.h @@ -28,8 +28,14 @@ class VrRecoveryUI : public ScreenRecoveryUI { // Can vary per device depending on screen size and lens distortion. const int kStereoOffset; - bool InitTextParams() override; + int ScreenWidth() const override; + int ScreenHeight() const override; + void DrawSurface(GRSurface* surface, int sx, int sy, int w, int h, int dx, int dy) const override; + int DrawHorizontalRule(int y) const override; + void DrawHighlightBar(int x, int y, int width, int height) const override; + void DrawFill(int x, int y, int w, int h) const override; + void DrawTextIcon(int x, int y, GRSurface* surface) const override; int DrawTextLine(int x, int y, const char* line, bool bold) const override; }; -- cgit v1.2.3 From 0bf20d51336aef08850d0152a6e1501f4b733cf6 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 5 Oct 2017 12:06:49 -0700 Subject: Don't include "error_code.h" in edify/expr.h. Use forward declartion to avoid pull in the module that contains error_code.h (trying to move it into libotautil). Otherwise all the modules that include "edify/expr.h" need to depend on the module that exports error_code.h. .cpp sources should include "error_code.h" explicitly to use the enums. Test: lunch aosp_{angler,bullhead,dragon,fugu,sailfish}-userdebug; mmma bootable/recovery Change-Id: Ic82db2746c7deb866e8cdfb3c57e0b1ecc71c4dc --- edify/expr.cpp | 9 ++++----- edify/expr.h | 38 ++++++++++++++++++++------------------ error_code.h | 6 +++--- updater/updater.cpp | 1 + 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/edify/expr.cpp b/edify/expr.cpp index 54ab3325c..403162d6d 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -31,6 +31,8 @@ #include #include +#include "error_code.h" + // Functions should: // // - return a malloc()'d string @@ -416,8 +418,5 @@ Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) { return nullptr; } -State::State(const std::string& script, void* cookie) : - script(script), - cookie(cookie) { -} - +State::State(const std::string& script, void* cookie) + : script(script), cookie(cookie), error_code(kNoError), cause_code(kNoCause) {} diff --git a/edify/expr.h b/edify/expr.h index 4838d20c0..32828028a 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -23,32 +23,34 @@ #include #include -#include "error_code.h" +// Forward declaration to avoid including "error_code.h". +enum ErrorCode : int; +enum CauseCode : int; struct State { - State(const std::string& script, void* cookie); + State(const std::string& script, void* cookie); - // The source of the original script. - const std::string& script; + // The source of the original script. + const std::string& script; - // Optional pointer to app-specific data; the core of edify never - // uses this value. - void* cookie; + // Optional pointer to app-specific data; the core of edify never + // uses this value. + void* cookie; - // The error message (if any) returned if the evaluation aborts. - // Should be empty initially, will be either empty or a string that - // Evaluate() returns. - std::string errmsg; + // The error message (if any) returned if the evaluation aborts. + // Should be empty initially, will be either empty or a string that + // Evaluate() returns. + std::string errmsg; - // error code indicates the type of failure (e.g. failure to update system image) - // during the OTA process. - ErrorCode error_code = kNoError; + // error code indicates the type of failure (e.g. failure to update system image) + // during the OTA process. + ErrorCode error_code; - // cause code provides more detailed reason of an OTA failure (e.g. fsync error) - // in addition to the error code. - CauseCode cause_code = kNoCause; + // cause code provides more detailed reason of an OTA failure (e.g. fsync error) + // in addition to the error code. + CauseCode cause_code; - bool is_retry = false; + bool is_retry = false; }; enum ValueType { diff --git a/error_code.h b/error_code.h index 4e3032bc9..26b9bb40d 100644 --- a/error_code.h +++ b/error_code.h @@ -17,7 +17,7 @@ #ifndef _ERROR_CODE_H_ #define _ERROR_CODE_H_ -enum ErrorCode { +enum ErrorCode : int { kNoError = -1, kLowBattery = 20, kZipVerificationFailure, @@ -30,7 +30,7 @@ enum ErrorCode { kUpdateBinaryCommandFailure, }; -enum CauseCode { +enum CauseCode : int { kNoCause = -1, kArgsParsingFailure = 100, kStashCreationFailure, @@ -51,7 +51,7 @@ enum CauseCode { kVendorFailure = 200 }; -enum UncryptErrorCode { +enum UncryptErrorCode : int { kUncryptNoError = -1, kUncryptErrorPlaceholder = 50, kUncryptTimeoutError = 100, diff --git a/updater/updater.cpp b/updater/updater.cpp index e10174f71..309c309a5 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -31,6 +31,7 @@ #include #include "edify/expr.h" +#include "error_code.h" #include "otafault/config.h" #include "otautil/DirUtil.h" #include "otautil/SysUtil.h" -- cgit v1.2.3 From 38d78d19b9482e7bbad756f0cdbf533cbb52a8f8 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 9 Oct 2017 11:03:38 -0700 Subject: applypatch: Forward declare struct Value. And move '#include "edify/expr.h"' into .cpp files. This breaks the transitive dependency on libedify. Modules that include "applypatch/applypatch.h" don't need to add libedify into their dependency list, unless they really need anything from libedify. Build libedify static library for host, which is needed by libimgpatch. Test: mmma bootable/recovery Change-Id: Ibb53d322579fcbf593438d058d9bcee240625941 --- applypatch/Android.mk | 2 ++ applypatch/bspatch.cpp | 3 ++- applypatch/imgpatch.cpp | 2 ++ applypatch/include/applypatch/applypatch.h | 3 ++- edify/Android.mk | 16 ++++++++++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/applypatch/Android.mk b/applypatch/Android.mk index f5dda2bc4..db72e8eee 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -53,6 +53,7 @@ LOCAL_C_INCLUDES := \ bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ + libedify \ libcrypto \ libbspatch \ libbase \ @@ -77,6 +78,7 @@ LOCAL_C_INCLUDES := \ bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ + libedify \ libcrypto \ libbspatch \ libbase \ diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index 65ee614ef..b2f29fbd6 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -30,6 +30,7 @@ #include #include "applypatch/applypatch.h" +#include "edify/expr.h" #include "print_sha1.h" void ShowBSDiffLicense() { @@ -91,4 +92,4 @@ int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value } } return result; -} \ No newline at end of file +} diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index df75f98d4..7a43ddbef 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -37,6 +37,8 @@ #include #include +#include "edify/expr.h" + static inline int64_t Read8(const void *address) { return android::base::get_unaligned(address); } diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 581360ef1..2a3b3ef39 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -27,7 +27,8 @@ #include -#include "edify/expr.h" +// Forward declaration to avoid including "edify/expr.h" in the header. +struct Value; struct FileContents { uint8_t sha1[SHA_DIGEST_LENGTH]; diff --git a/edify/Android.mk b/edify/Android.mk index cec65f42a..fbf2b1b06 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -34,3 +34,19 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. LOCAL_STATIC_LIBRARIES += libbase include $(BUILD_STATIC_LIBRARY) + +# +# Build the host-side library (static library) +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(edify_src_files) + +LOCAL_CFLAGS := -Wall -Werror +LOCAL_CPPFLAGS := -Wno-unused-parameter +LOCAL_CPPFLAGS += -Wno-deprecated-register +LOCAL_MODULE := libedify +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES += libbase + +include $(BUILD_HOST_STATIC_LIBRARY) -- cgit v1.2.3 From 1fc5bf353a8719d16fd9ba29a661d211bad4038f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 6 Oct 2017 07:43:41 -0700 Subject: Revert "Revert "Move error_code.h into otautil."" This reverts commit 26436d6d6010d5323349af7e119ff8f34f85c40c to re-land "Move error_code.h into otautil.". This way it stops requiring relative path ".." in LOCAL_C_INCLUDES (uncrypt and edify). Soong doesn't accept non-local ".." in "local_include_dirs". This CL needs to land with device-specific module changes (e.g. adding the dependency on libotautil). Test: lunch aosp_{angler,bullhead,dragon,fugu,sailfish}-userdebug; mmma bootable/recovery Change-Id: If193241801af2dae73eccd31ce57cd2b81c9fd96 --- applypatch/Android.mk | 1 + edify/Android.mk | 5 ++- edify/expr.cpp | 2 +- edify/expr.h | 2 +- error_code.h | 78 ------------------------------------ install.cpp | 2 +- otautil/Android.bp | 2 + otautil/include/otautil/error_code.h | 78 ++++++++++++++++++++++++++++++++++++ recovery.cpp | 2 +- tests/component/updater_test.cpp | 2 +- uncrypt/Android.mk | 2 +- uncrypt/uncrypt.cpp | 2 +- updater/blockimg.cpp | 2 +- updater/install.cpp | 2 +- updater/updater.cpp | 2 +- 15 files changed, 94 insertions(+), 90 deletions(-) delete mode 100644 error_code.h create mode 100644 otautil/include/otautil/error_code.h diff --git a/applypatch/Android.mk b/applypatch/Android.mk index db72e8eee..4eb0111df 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -116,6 +116,7 @@ LOCAL_STATIC_LIBRARIES := \ libapplypatch \ libedify \ libotafault \ + libotautil \ libbspatch \ libbase \ libziparchive \ diff --git a/edify/Android.mk b/edify/Android.mk index fbf2b1b06..527698e68 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -30,8 +30,9 @@ LOCAL_CFLAGS := -Wall -Werror LOCAL_CPPFLAGS := -Wno-unused-parameter LOCAL_CPPFLAGS += -Wno-deprecated-register LOCAL_MODULE := libedify -LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -LOCAL_STATIC_LIBRARIES += libbase +LOCAL_STATIC_LIBRARIES += \ + libotautil \ + libbase include $(BUILD_STATIC_LIBRARY) diff --git a/edify/expr.cpp b/edify/expr.cpp index 403162d6d..104c0fd90 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -31,7 +31,7 @@ #include #include -#include "error_code.h" +#include "otautil/error_code.h" // Functions should: // diff --git a/edify/expr.h b/edify/expr.h index 32828028a..770d1cf0d 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -23,7 +23,7 @@ #include #include -// Forward declaration to avoid including "error_code.h". +// Forward declaration to avoid including "otautil/error_code.h". enum ErrorCode : int; enum CauseCode : int; diff --git a/error_code.h b/error_code.h deleted file mode 100644 index 26b9bb40d..000000000 --- a/error_code.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _ERROR_CODE_H_ -#define _ERROR_CODE_H_ - -enum ErrorCode : int { - kNoError = -1, - kLowBattery = 20, - kZipVerificationFailure, - kZipOpenFailure, - kBootreasonInBlacklist, - kPackageCompatibilityFailure, - kScriptExecutionFailure, - kMapFileFailure, - kForkUpdateBinaryFailure, - kUpdateBinaryCommandFailure, -}; - -enum CauseCode : int { - kNoCause = -1, - kArgsParsingFailure = 100, - kStashCreationFailure, - kFileOpenFailure, - kLseekFailure, - kFreadFailure, - kFwriteFailure, - kFsyncFailure, - kLibfecFailure, - kFileGetPropFailure, - kFileRenameFailure, - kSymlinkFailure, - kSetMetadataFailure, - kTune2FsFailure, - kRebootFailure, - kPackageExtractFileFailure, - kPatchApplicationFailure, - kVendorFailure = 200 -}; - -enum UncryptErrorCode : int { - kUncryptNoError = -1, - kUncryptErrorPlaceholder = 50, - kUncryptTimeoutError = 100, - kUncryptFileRemoveError, - kUncryptFileOpenError, - kUncryptSocketOpenError, - kUncryptSocketWriteError, - kUncryptSocketListenError, - kUncryptSocketAcceptError, - kUncryptFstabReadError, - kUncryptFileStatError, - kUncryptBlockOpenError, - kUncryptIoctlError, - kUncryptReadError, - kUncryptWriteError, - kUncryptFileSyncError, - kUncryptFileCloseError, - kUncryptFileRenameError, - kUncryptPackageMissingError, - kUncryptRealpathFindError, - kUncryptBlockDeviceFindError, -}; - -#endif // _ERROR_CODE_H_ diff --git a/install.cpp b/install.cpp index 74d1a68b3..d05893171 100644 --- a/install.cpp +++ b/install.cpp @@ -49,9 +49,9 @@ #include #include "common.h" -#include "error_code.h" #include "otautil/SysUtil.h" #include "otautil/ThermalUtil.h" +#include "otautil/error_code.h" #include "private/install.h" #include "roots.h" #include "ui.h" diff --git a/otautil/Android.bp b/otautil/Android.bp index 9cde7baa7..5905ba649 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -15,6 +15,8 @@ cc_library_static { name: "libotautil", + host_supported: true, + srcs: [ "SysUtil.cpp", "DirUtil.cpp", diff --git a/otautil/include/otautil/error_code.h b/otautil/include/otautil/error_code.h new file mode 100644 index 000000000..b0ff42d8d --- /dev/null +++ b/otautil/include/otautil/error_code.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ERROR_CODE_H_ +#define _ERROR_CODE_H_ + +enum ErrorCode : int { + kNoError = -1, + kLowBattery = 20, + kZipVerificationFailure, + kZipOpenFailure, + kBootreasonInBlacklist, + kPackageCompatibilityFailure, + kScriptExecutionFailure, + kMapFileFailure, + kForkUpdateBinaryFailure, + kUpdateBinaryCommandFailure, +}; + +enum CauseCode : int { + kNoCause = -1, + kArgsParsingFailure = 100, + kStashCreationFailure, + kFileOpenFailure, + kLseekFailure, + kFreadFailure, + kFwriteFailure, + kFsyncFailure, + kLibfecFailure, + kFileGetPropFailure, + kFileRenameFailure, + kSymlinkFailure, + kSetMetadataFailure, + kTune2FsFailure, + kRebootFailure, + kPackageExtractFileFailure, + kPatchApplicationFailure, + kVendorFailure = 200 +}; + +enum UncryptErrorCode : int { + kUncryptNoError = -1, + kUncryptErrorPlaceholder = 50, + kUncryptTimeoutError = 100, + kUncryptFileRemoveError, + kUncryptFileOpenError, + kUncryptSocketOpenError, + kUncryptSocketWriteError, + kUncryptSocketListenError, + kUncryptSocketAcceptError, + kUncryptFstabReadError, + kUncryptFileStatError, + kUncryptBlockOpenError, + kUncryptIoctlError, + kUncryptReadError, + kUncryptWriteError, + kUncryptFileSyncError, + kUncryptFileCloseError, + kUncryptFileRenameError, + kUncryptPackageMissingError, + kUncryptRealpathFindError, + kUncryptBlockDeviceFindError, +}; + +#endif // _ERROR_CODE_H_ diff --git a/recovery.cpp b/recovery.cpp index 4dc5b540e..243ee4af3 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -61,13 +61,13 @@ #include "adb_install.h" #include "common.h" #include "device.h" -#include "error_code.h" #include "fuse_sdcard_provider.h" #include "fuse_sideload.h" #include "install.h" #include "minadbd/minadbd.h" #include "minui/minui.h" #include "otautil/DirUtil.h" +#include "otautil/error_code.h" #include "roots.h" #include "rotate_logs.h" #include "screen_ui.h" diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 2a0575a31..e35870dc7 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -39,8 +39,8 @@ #include "common/test_constants.h" #include "edify/expr.h" -#include "error_code.h" #include "otautil/SysUtil.h" +#include "otautil/error_code.h" #include "print_sha1.h" #include "updater/blockimg.h" #include "updater/install.h" diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index a3b0ca98d..601f9276e 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -17,10 +17,10 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := uncrypt.cpp -LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. LOCAL_MODULE := uncrypt LOCAL_STATIC_LIBRARIES := \ libbootloader_message \ + libotautil \ libbase \ liblog \ libfs_mgr \ diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 7a2ccbc7c..645faadbf 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -116,7 +116,7 @@ #include #include -#include "error_code.h" +#include "otautil/error_code.h" static constexpr int WINDOW_SIZE = 5; static constexpr int FIBMAP_RETRY_LIMIT = 3; diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 696cddf41..0f8364441 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -50,8 +50,8 @@ #include #include "edify/expr.h" -#include "error_code.h" #include "otafault/ota_io.h" +#include "otautil/error_code.h" #include "print_sha1.h" #include "rangeset.h" #include "updater/install.h" diff --git a/updater/install.cpp b/updater/install.cpp index fc085d5aa..01210f51c 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -57,10 +57,10 @@ #include #include "edify/expr.h" -#include "error_code.h" #include "mounts.h" #include "otafault/ota_io.h" #include "otautil/DirUtil.h" +#include "otautil/error_code.h" #include "print_sha1.h" #include "tune2fs.h" #include "updater/updater.h" diff --git a/updater/updater.cpp b/updater/updater.cpp index 309c309a5..f55a0d3bd 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -31,10 +31,10 @@ #include #include "edify/expr.h" -#include "error_code.h" #include "otafault/config.h" #include "otautil/DirUtil.h" #include "otautil/SysUtil.h" +#include "otautil/error_code.h" #include "updater/blockimg.h" #include "updater/install.h" -- cgit v1.2.3 From e6f7f95d3493b47514a6104e11d206a455a6433e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 4 Oct 2017 09:33:01 -0700 Subject: edify: Export the header and move to Soong. Also make matching changes to applypatch modules which include edify/expr.h. Test: mmma bootable/recovery Change-Id: Ia72be3caa010d7f56a70add2da345e631b306378 --- Android.bp | 1 + Android.mk | 1 - applypatch/Android.mk | 1 + edify/Android.bp | 45 +++++++++++++ edify/Android.mk | 53 --------------- edify/expr.cpp | 2 +- edify/expr.h | 161 --------------------------------------------- edify/include/edify/expr.h | 161 +++++++++++++++++++++++++++++++++++++++++++++ edify/lexer.ll | 2 +- edify/parser.yy | 2 +- 10 files changed, 211 insertions(+), 218 deletions(-) create mode 100644 edify/Android.bp delete mode 100644 edify/Android.mk delete mode 100644 edify/expr.h create mode 100644 edify/include/edify/expr.h diff --git a/Android.bp b/Android.bp index 99ca3a45c..4c66c427e 100644 --- a/Android.bp +++ b/Android.bp @@ -1,5 +1,6 @@ subdirs = [ "bootloader_message", + "edify", "otafault", "otautil", ] diff --git a/Android.mk b/Android.mk index ac72bac01..b40e23dc4 100644 --- a/Android.mk +++ b/Android.mk @@ -261,7 +261,6 @@ include $(BUILD_STATIC_LIBRARY) include \ $(LOCAL_PATH)/applypatch/Android.mk \ $(LOCAL_PATH)/boot_control/Android.mk \ - $(LOCAL_PATH)/edify/Android.mk \ $(LOCAL_PATH)/minadbd/Android.mk \ $(LOCAL_PATH)/minui/Android.mk \ $(LOCAL_PATH)/tests/Android.mk \ diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 4eb0111df..05d937004 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -29,6 +29,7 @@ LOCAL_C_INCLUDES := \ bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ + libedify \ libotafault \ libbase \ libcrypto \ diff --git a/edify/Android.bp b/edify/Android.bp new file mode 100644 index 000000000..42947eb4e --- /dev/null +++ b/edify/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_static { + name: "libedify", + + host_supported: true, + + srcs: [ + "expr.cpp", + "lexer.ll", + "parser.yy", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wno-deprecated-register", + "-Wno-unused-parameter", + ], + + export_include_dirs: [ + "include", + ], + + local_include_dirs: [ + "include", + ], + + static_libs: [ + "libbase", + "libotautil", + ], +} diff --git a/edify/Android.mk b/edify/Android.mk deleted file mode 100644 index 527698e68..000000000 --- a/edify/Android.mk +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2009 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -edify_src_files := \ - lexer.ll \ - parser.yy \ - expr.cpp - -# -# Build the device-side library (static library) -# -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(edify_src_files) - -LOCAL_CFLAGS := -Wall -Werror -LOCAL_CPPFLAGS := -Wno-unused-parameter -LOCAL_CPPFLAGS += -Wno-deprecated-register -LOCAL_MODULE := libedify -LOCAL_STATIC_LIBRARIES += \ - libotautil \ - libbase - -include $(BUILD_STATIC_LIBRARY) - -# -# Build the host-side library (static library) -# -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(edify_src_files) - -LOCAL_CFLAGS := -Wall -Werror -LOCAL_CPPFLAGS := -Wno-unused-parameter -LOCAL_CPPFLAGS += -Wno-deprecated-register -LOCAL_MODULE := libedify -LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -LOCAL_STATIC_LIBRARIES += libbase - -include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/edify/expr.cpp b/edify/expr.cpp index 104c0fd90..1b8623f03 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "expr.h" +#include "edify/expr.h" #include #include diff --git a/edify/expr.h b/edify/expr.h deleted file mode 100644 index 770d1cf0d..000000000 --- a/edify/expr.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _EXPRESSION_H -#define _EXPRESSION_H - -#include - -#include -#include -#include - -// Forward declaration to avoid including "otautil/error_code.h". -enum ErrorCode : int; -enum CauseCode : int; - -struct State { - State(const std::string& script, void* cookie); - - // The source of the original script. - const std::string& script; - - // Optional pointer to app-specific data; the core of edify never - // uses this value. - void* cookie; - - // The error message (if any) returned if the evaluation aborts. - // Should be empty initially, will be either empty or a string that - // Evaluate() returns. - std::string errmsg; - - // error code indicates the type of failure (e.g. failure to update system image) - // during the OTA process. - ErrorCode error_code; - - // cause code provides more detailed reason of an OTA failure (e.g. fsync error) - // in addition to the error code. - CauseCode cause_code; - - bool is_retry = false; -}; - -enum ValueType { - VAL_INVALID = -1, - VAL_STRING = 1, - VAL_BLOB = 2, -}; - -struct Value { - ValueType type; - std::string data; - - Value(ValueType type, const std::string& str) : - type(type), - data(str) {} -}; - -struct Expr; - -using Function = Value* (*)(const char* name, State* state, - const std::vector>& argv); - -struct Expr { - Function fn; - std::string name; - std::vector> argv; - int start, end; - - Expr(Function fn, const std::string& name, int start, int end) : - fn(fn), - name(name), - start(start), - end(end) {} -}; - -// Evaluate the input expr, return the resulting Value. -Value* EvaluateValue(State* state, const std::unique_ptr& expr); - -// Evaluate the input expr, assert that it is a string, and update the result parameter. This -// function returns true if the evaluation succeeds. This is a convenience function for older -// functions that want to deal only with strings. -bool Evaluate(State* state, const std::unique_ptr& expr, std::string* result); - -// Glue to make an Expr out of a literal. -Value* Literal(const char* name, State* state, const std::vector>& argv); - -// Functions corresponding to various syntactic sugar operators. -// ("concat" is also available as a builtin function, to concatenate -// more than two strings.) -Value* ConcatFn(const char* name, State* state, const std::vector>& argv); -Value* LogicalAndFn(const char* name, State* state, const std::vector>& argv); -Value* LogicalOrFn(const char* name, State* state, const std::vector>& argv); -Value* LogicalNotFn(const char* name, State* state, const std::vector>& argv); -Value* SubstringFn(const char* name, State* state, const std::vector>& argv); -Value* EqualityFn(const char* name, State* state, const std::vector>& argv); -Value* InequalityFn(const char* name, State* state, const std::vector>& argv); -Value* SequenceFn(const char* name, State* state, const std::vector>& argv); - -// Global builtins, registered by RegisterBuiltins(). -Value* IfElseFn(const char* name, State* state, const std::vector>& argv); -Value* AssertFn(const char* name, State* state, const std::vector>& argv); -Value* AbortFn(const char* name, State* state, const std::vector>& argv); - -// Register a new function. The same Function may be registered under -// multiple names, but a given name should only be used once. -void RegisterFunction(const std::string& name, Function fn); - -// Register all the builtins. -void RegisterBuiltins(); - -// Find the Function for a given name; return NULL if no such function -// exists. -Function FindFunction(const std::string& name); - -// --- convenience functions for use in functions --- - -// Evaluate the expressions in argv, and put the results of strings in args. If any expression -// evaluates to nullptr, return false. Return true on success. -bool ReadArgs(State* state, const std::vector>& argv, - std::vector* args); -bool ReadArgs(State* state, const std::vector>& argv, - std::vector* args, size_t start, size_t len); - -// Evaluate the expressions in argv, and put the results of Value* in args. If any -// expression evaluate to nullptr, return false. Return true on success. -bool ReadValueArgs(State* state, const std::vector>& argv, - std::vector>* args); -bool ReadValueArgs(State* state, const std::vector>& argv, - std::vector>* args, size_t start, size_t len); - -// Use printf-style arguments to compose an error message to put into -// *state. Returns NULL. -Value* ErrorAbort(State* state, const char* format, ...) - __attribute__((format(printf, 2, 3), deprecated)); - -// ErrorAbort has an optional (but recommended) argument 'cause_code'. If the cause code -// is set, it will be logged into last_install and provides reason of OTA failures. -Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) - __attribute__((format(printf, 3, 4))); - -// Copying the string into a Value. -Value* StringValue(const char* str); - -Value* StringValue(const std::string& str); - -int parse_string(const char* str, std::unique_ptr* root, int* error_count); - -#endif // _EXPRESSION_H diff --git a/edify/include/edify/expr.h b/edify/include/edify/expr.h new file mode 100644 index 000000000..770d1cf0d --- /dev/null +++ b/edify/include/edify/expr.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _EXPRESSION_H +#define _EXPRESSION_H + +#include + +#include +#include +#include + +// Forward declaration to avoid including "otautil/error_code.h". +enum ErrorCode : int; +enum CauseCode : int; + +struct State { + State(const std::string& script, void* cookie); + + // The source of the original script. + const std::string& script; + + // Optional pointer to app-specific data; the core of edify never + // uses this value. + void* cookie; + + // The error message (if any) returned if the evaluation aborts. + // Should be empty initially, will be either empty or a string that + // Evaluate() returns. + std::string errmsg; + + // error code indicates the type of failure (e.g. failure to update system image) + // during the OTA process. + ErrorCode error_code; + + // cause code provides more detailed reason of an OTA failure (e.g. fsync error) + // in addition to the error code. + CauseCode cause_code; + + bool is_retry = false; +}; + +enum ValueType { + VAL_INVALID = -1, + VAL_STRING = 1, + VAL_BLOB = 2, +}; + +struct Value { + ValueType type; + std::string data; + + Value(ValueType type, const std::string& str) : + type(type), + data(str) {} +}; + +struct Expr; + +using Function = Value* (*)(const char* name, State* state, + const std::vector>& argv); + +struct Expr { + Function fn; + std::string name; + std::vector> argv; + int start, end; + + Expr(Function fn, const std::string& name, int start, int end) : + fn(fn), + name(name), + start(start), + end(end) {} +}; + +// Evaluate the input expr, return the resulting Value. +Value* EvaluateValue(State* state, const std::unique_ptr& expr); + +// Evaluate the input expr, assert that it is a string, and update the result parameter. This +// function returns true if the evaluation succeeds. This is a convenience function for older +// functions that want to deal only with strings. +bool Evaluate(State* state, const std::unique_ptr& expr, std::string* result); + +// Glue to make an Expr out of a literal. +Value* Literal(const char* name, State* state, const std::vector>& argv); + +// Functions corresponding to various syntactic sugar operators. +// ("concat" is also available as a builtin function, to concatenate +// more than two strings.) +Value* ConcatFn(const char* name, State* state, const std::vector>& argv); +Value* LogicalAndFn(const char* name, State* state, const std::vector>& argv); +Value* LogicalOrFn(const char* name, State* state, const std::vector>& argv); +Value* LogicalNotFn(const char* name, State* state, const std::vector>& argv); +Value* SubstringFn(const char* name, State* state, const std::vector>& argv); +Value* EqualityFn(const char* name, State* state, const std::vector>& argv); +Value* InequalityFn(const char* name, State* state, const std::vector>& argv); +Value* SequenceFn(const char* name, State* state, const std::vector>& argv); + +// Global builtins, registered by RegisterBuiltins(). +Value* IfElseFn(const char* name, State* state, const std::vector>& argv); +Value* AssertFn(const char* name, State* state, const std::vector>& argv); +Value* AbortFn(const char* name, State* state, const std::vector>& argv); + +// Register a new function. The same Function may be registered under +// multiple names, but a given name should only be used once. +void RegisterFunction(const std::string& name, Function fn); + +// Register all the builtins. +void RegisterBuiltins(); + +// Find the Function for a given name; return NULL if no such function +// exists. +Function FindFunction(const std::string& name); + +// --- convenience functions for use in functions --- + +// Evaluate the expressions in argv, and put the results of strings in args. If any expression +// evaluates to nullptr, return false. Return true on success. +bool ReadArgs(State* state, const std::vector>& argv, + std::vector* args); +bool ReadArgs(State* state, const std::vector>& argv, + std::vector* args, size_t start, size_t len); + +// Evaluate the expressions in argv, and put the results of Value* in args. If any +// expression evaluate to nullptr, return false. Return true on success. +bool ReadValueArgs(State* state, const std::vector>& argv, + std::vector>* args); +bool ReadValueArgs(State* state, const std::vector>& argv, + std::vector>* args, size_t start, size_t len); + +// Use printf-style arguments to compose an error message to put into +// *state. Returns NULL. +Value* ErrorAbort(State* state, const char* format, ...) + __attribute__((format(printf, 2, 3), deprecated)); + +// ErrorAbort has an optional (but recommended) argument 'cause_code'. If the cause code +// is set, it will be logged into last_install and provides reason of OTA failures. +Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) + __attribute__((format(printf, 3, 4))); + +// Copying the string into a Value. +Value* StringValue(const char* str); + +Value* StringValue(const std::string& str); + +int parse_string(const char* str, std::unique_ptr* root, int* error_count); + +#endif // _EXPRESSION_H diff --git a/edify/lexer.ll b/edify/lexer.ll index cb4594371..4e04003b1 100644 --- a/edify/lexer.ll +++ b/edify/lexer.ll @@ -18,7 +18,7 @@ #include #include -#include "expr.h" +#include "edify/expr.h" #include "yydefs.h" #include "parser.h" diff --git a/edify/parser.yy b/edify/parser.yy index b1685eb1f..bd2e0105f 100644 --- a/edify/parser.yy +++ b/edify/parser.yy @@ -25,7 +25,7 @@ #include -#include "expr.h" +#include "edify/expr.h" #include "yydefs.h" #include "parser.h" -- cgit v1.2.3 From c13d2ec7726bae1a6ec0d29c4b4ed54ac46149a1 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 10 Oct 2017 10:56:09 -0700 Subject: otautil: Fix mac build. bootable/recovery/otautil/SysUtil.cpp:103:19: error: use of undeclared identifier 'mmap64'; did you mean 'mmap'? void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); ^~~~~~ Test: mmma bootable/recovery Change-Id: I22d7dc4d994069201e5a633cec21421e2c4182fa --- otautil/Android.bp | 1 + otautil/SysUtil.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/otautil/Android.bp b/otautil/Android.bp index 5905ba649..659fefada 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -29,6 +29,7 @@ cc_library_static { ], cflags: [ + "-D_FILE_OFFSET_BITS=64", "-Werror", "-Wall", ], diff --git a/otautil/SysUtil.cpp b/otautil/SysUtil.cpp index d54a824d2..0655c4778 100644 --- a/otautil/SysUtil.cpp +++ b/otautil/SysUtil.cpp @@ -100,7 +100,7 @@ bool MemMapping::MapBlockFile(const std::string& filename) { } // Reserve enough contiguous address space for the whole file. - void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + void* reserve = mmap(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); if (reserve == MAP_FAILED) { PLOG(ERROR) << "failed to reserve address space"; return false; @@ -135,8 +135,8 @@ bool MemMapping::MapBlockFile(const std::string& filename) { break; } - void* range_start = mmap64(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, - static_cast(start) * blksize); + void* range_start = mmap(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, + static_cast(start) * blksize); if (range_start == MAP_FAILED) { PLOG(ERROR) << "failed to map range " << i << ": " << line; success = false; -- cgit v1.2.3 From 3db3205b2ae760550fe93a0b50ae2656190d4844 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 10 Oct 2017 15:02:13 -0700 Subject: applypatch: Use shared libs for libbase/libcrypto/liblog/libziparchive. This reduces the size of /system/bin/applypatch by ~69KB (aosp_bullhead-userdebug). Also remove the unneeded libcutils dependency. Test: mmma bootable/recovery Test: Check that /system/bin/install-recovery.sh successfully installs the recovery image. Change-Id: I5063be9a9b7b8029d45ab5c2a7c45ef2cda81d26 --- applypatch/Android.mk | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 05d937004..5b92bade3 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -111,7 +111,6 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := applypatch_main.cpp LOCAL_MODULE := applypatch -LOCAL_C_INCLUDES := bootable/recovery LOCAL_STATIC_LIBRARIES := \ libapplypatch_modes \ libapplypatch \ @@ -119,15 +118,13 @@ LOCAL_STATIC_LIBRARIES := \ libotafault \ libotautil \ libbspatch \ - libbase \ - libziparchive \ - liblog \ - libcrypto \ libbz LOCAL_SHARED_LIBRARIES := \ libbase \ + libcrypto \ + liblog \ libz \ - libcutils + libziparchive LOCAL_CFLAGS := -Wall -Werror include $(BUILD_EXECUTABLE) -- cgit v1.2.3 From 09e468f84cc245fba61d69165b4af8f1ec4cdfd5 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 29 Sep 2017 14:39:33 -0700 Subject: Move rangeset.h and print_sha1.h into otautil. Also drop the "bootable/recovery" path in LOCAL_C_INCLUDES from applypatch modules. Test: lunch aosp_{angler,bullhead,fugu,dragon,sailfish}-userdebug; mmma bootable/recovery Change-Id: Idd602a796894f971ee4f8fa3eafe36c42d9de986 --- Android.mk | 1 + applypatch/Android.mk | 34 ++-- applypatch/applypatch.cpp | 2 +- applypatch/bspatch.cpp | 2 +- applypatch/imgdiff.cpp | 2 +- applypatch/include/applypatch/imgdiff_image.h | 2 +- otautil/include/otautil/print_sha1.h | 47 +++++ otautil/include/otautil/rangeset.h | 278 ++++++++++++++++++++++++++ print_sha1.h | 47 ----- rangeset.h | 278 -------------------------- tests/Android.mk | 1 + tests/component/applypatch_test.cpp | 2 +- tests/component/updater_test.cpp | 2 +- tests/unit/rangeset_test.cpp | 4 +- updater/blockimg.cpp | 4 +- updater/install.cpp | 4 +- verifier.cpp | 2 +- 17 files changed, 357 insertions(+), 355 deletions(-) create mode 100644 otautil/include/otautil/print_sha1.h create mode 100644 otautil/include/otautil/rangeset.h delete mode 100644 print_sha1.h delete mode 100644 rangeset.h diff --git a/Android.mk b/Android.mk index b40e23dc4..5fe37fd60 100644 --- a/Android.mk +++ b/Android.mk @@ -229,6 +229,7 @@ LOCAL_SRC_FILES := \ asn1_decoder.cpp \ verifier.cpp LOCAL_STATIC_LIBRARIES := \ + libotautil \ libcrypto_utils \ libcrypto \ libbase diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 05d937004..66494e607 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -25,12 +25,13 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := libapplypatch LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - bootable/recovery -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libedify \ libotafault \ + libotautil \ libbase \ libcrypto \ libbspatch \ @@ -50,11 +51,12 @@ LOCAL_SRC_FILES := \ imgpatch.cpp LOCAL_MODULE := libimgpatch LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - bootable/recovery -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libedify \ + libotautil \ libcrypto \ libbspatch \ libbase \ @@ -75,11 +77,12 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := libimgpatch LOCAL_MODULE_HOST_OS := linux LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - bootable/recovery -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := \ libedify \ + libotautil \ libcrypto \ libbspatch \ libbase \ @@ -97,9 +100,9 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ applypatch_modes.cpp LOCAL_MODULE := libapplypatch_modes -LOCAL_C_INCLUDES := bootable/recovery LOCAL_STATIC_LIBRARIES := \ libapplypatch \ + libotautil \ libbase \ libedify \ libcrypto @@ -111,7 +114,6 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := applypatch_main.cpp LOCAL_MODULE := applypatch -LOCAL_C_INCLUDES := bootable/recovery LOCAL_STATIC_LIBRARIES := \ libapplypatch_modes \ libapplypatch \ @@ -141,6 +143,7 @@ libimgdiff_cflags := \ -DZLIB_CONST libimgdiff_static_libraries := \ + libotautil \ libbsdiff \ libdivsufsort \ libdivsufsort64 \ @@ -161,8 +164,7 @@ LOCAL_CFLAGS := \ LOCAL_STATIC_LIBRARIES := \ $(libimgdiff_static_libraries) LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - bootable/recovery + $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_STATIC_LIBRARY) @@ -177,8 +179,7 @@ LOCAL_CFLAGS := \ LOCAL_STATIC_LIBRARIES := \ $(libimgdiff_static_libraries) LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - bootable/recovery + $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_HOST_STATIC_LIBRARY) @@ -193,6 +194,5 @@ LOCAL_STATIC_LIBRARIES := \ $(libimgdiff_static_libraries) \ libbz LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include \ - bootable/recovery + $(LOCAL_PATH)/include include $(BUILD_HOST_EXECUTABLE) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 729d2a910..c8b75df79 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -40,7 +40,7 @@ #include "edify/expr.h" #include "otafault/ota_io.h" -#include "print_sha1.h" +#include "otautil/print_sha1.h" static int LoadPartitionContents(const std::string& filename, FileContents* file); static size_t FileSink(const unsigned char* data, size_t len, int fd); diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index b2f29fbd6..c291464a8 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -31,7 +31,7 @@ #include "applypatch/applypatch.h" #include "edify/expr.h" -#include "print_sha1.h" +#include "otautil/print_sha1.h" void ShowBSDiffLicense() { puts("The bsdiff library used herein is:\n" diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index c887a854d..3a61a7d0d 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -166,7 +166,7 @@ #include #include "applypatch/imgdiff_image.h" -#include "rangeset.h" +#include "otautil/rangeset.h" using android::base::get_unaligned; diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h index 491043dc1..3d29547cb 100644 --- a/applypatch/include/applypatch/imgdiff_image.h +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -29,7 +29,7 @@ #include #include "imgdiff.h" -#include "rangeset.h" +#include "otautil/rangeset.h" class ImageChunk { public: diff --git a/otautil/include/otautil/print_sha1.h b/otautil/include/otautil/print_sha1.h new file mode 100644 index 000000000..03a8d292a --- /dev/null +++ b/otautil/include/otautil/print_sha1.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RECOVERY_PRINT_SHA1_H +#define RECOVERY_PRINT_SHA1_H + +#include +#include + +#include + +static std::string print_sha1(const uint8_t* sha1, size_t len) { + const char* hex = "0123456789abcdef"; + std::string result = ""; + for (size_t i = 0; i < len; ++i) { + result.push_back(hex[(sha1[i] >> 4) & 0xf]); + result.push_back(hex[sha1[i] & 0xf]); + } + return result; +} + +[[maybe_unused]] static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { + return print_sha1(sha1, SHA_DIGEST_LENGTH); +} + +[[maybe_unused]] static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { + return print_sha1(sha1, 4); +} + +[[maybe_unused]] static std::string print_hex(const uint8_t* bytes, size_t len) { + return print_sha1(bytes, len); +} + +#endif // RECOVERY_PRINT_SHA1_H diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h new file mode 100644 index 000000000..f224a08be --- /dev/null +++ b/otautil/include/otautil/rangeset.h @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include + +using Range = std::pair; + +class RangeSet { + public: + RangeSet() : blocks_(0) {} + + explicit RangeSet(std::vector&& pairs) { + CHECK_NE(pairs.size(), static_cast(0)) << "Invalid number of tokens"; + + // Sanity check the input. + size_t result = 0; + for (const auto& range : pairs) { + CHECK_LT(range.first, range.second) + << "Empty or negative range: " << range.first << ", " << range.second; + size_t sz = range.second - range.first; + CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow"; + result += sz; + } + + ranges_ = pairs; + blocks_ = result; + } + + static RangeSet Parse(const std::string& range_text) { + std::vector pieces = android::base::Split(range_text, ","); + CHECK_GE(pieces.size(), static_cast(3)) << "Invalid range text: " << range_text; + + size_t num; + CHECK(android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) + << "Failed to parse the number of tokens: " << range_text; + + CHECK_NE(num, static_cast(0)) << "Invalid number of tokens: " << range_text; + CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; + CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; + + std::vector pairs; + for (size_t i = 0; i < num; i += 2) { + size_t first; + CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX))); + size_t second; + CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))); + + pairs.emplace_back(first, second); + } + + return RangeSet(std::move(pairs)); + } + + std::string ToString() const { + if (ranges_.empty()) { + return ""; + } + std::string result = std::to_string(ranges_.size() * 2); + for (const auto& r : ranges_) { + result += android::base::StringPrintf(",%zu,%zu", r.first, r.second); + } + + return result; + } + + // Get the block number for the i-th (starting from 0) block in the RangeSet. + size_t GetBlockNumber(size_t idx) const { + CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")"; + + for (const auto& range : ranges_) { + if (idx < range.second - range.first) { + return range.first + idx; + } + idx -= (range.second - range.first); + } + + CHECK(false) << "Failed to find block number for index " << idx; + return 0; // Unreachable, but to make compiler happy. + } + + // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" + // and "5,7" are not overlapped. + bool Overlaps(const RangeSet& other) const { + for (const auto& range : ranges_) { + size_t start = range.first; + size_t end = range.second; + for (const auto& other_range : other.ranges_) { + size_t other_start = other_range.first; + size_t other_end = other_range.second; + // [start, end) vs [other_start, other_end) + if (!(other_start >= end || start >= other_end)) { + return true; + } + } + } + return false; + } + + // size() gives the number of Range's in this RangeSet. + size_t size() const { + return ranges_.size(); + } + + // blocks() gives the number of all blocks in this RangeSet. + size_t blocks() const { + return blocks_; + } + + // We provide const iterators only. + std::vector::const_iterator cbegin() const { + return ranges_.cbegin(); + } + + std::vector::const_iterator cend() const { + return ranges_.cend(); + } + + // Need to provide begin()/end() since range-based loop expects begin()/end(). + std::vector::const_iterator begin() const { + return ranges_.cbegin(); + } + + std::vector::const_iterator end() const { + return ranges_.cend(); + } + + // Reverse const iterators for MoveRange(). + std::vector::const_reverse_iterator crbegin() const { + return ranges_.crbegin(); + } + + std::vector::const_reverse_iterator crend() const { + return ranges_.crend(); + } + + const Range& operator[](size_t i) const { + return ranges_[i]; + } + + bool operator==(const RangeSet& other) const { + // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5". + return (ranges_ == other.ranges_); + } + + bool operator!=(const RangeSet& other) const { + return ranges_ != other.ranges_; + } + + protected: + // Actual limit for each value and the total number are both INT_MAX. + std::vector ranges_; + size_t blocks_; +}; + +static constexpr size_t kBlockSize = 4096; + +// The class is a sorted version of a RangeSet; and it's useful in imgdiff to split the input +// files when we're handling large zip files. Specifically, we can treat the input file as a +// continuous RangeSet (i.e. RangeSet("0-99") for a 100 blocks file); and break it down into +// several smaller chunks based on the zip entries. + +// For example, [source: 0-99] can be split into +// [split_src1: 10-29]; [split_src2: 40-49, 60-69]; [split_src3: 70-89] +// Here "10-29" simply means block 10th to block 29th with respect to the original input file. +// Also, note that the split sources should be mutual exclusive, but they don't need to cover +// every block in the original source. +class SortedRangeSet : public RangeSet { + public: + SortedRangeSet() {} + + // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. + explicit SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { + std::sort(ranges_.begin(), ranges_.end()); + } + + void Insert(const Range& to_insert) { + SortedRangeSet rs({ to_insert }); + Insert(rs); + } + + // Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges. + void Insert(const SortedRangeSet& rs) { + if (rs.size() == 0) { + return; + } + // Merge and sort the two RangeSets. + std::vector temp = std::move(ranges_); + std::copy(rs.begin(), rs.end(), std::back_inserter(temp)); + std::sort(temp.begin(), temp.end()); + + Clear(); + // Trim overlaps and insert the result back to ranges_. + Range to_insert = temp.front(); + for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) { + if (it->first <= to_insert.second) { + to_insert.second = std::max(to_insert.second, it->second); + } else { + ranges_.push_back(to_insert); + blocks_ += (to_insert.second - to_insert.first); + to_insert = *it; + } + } + ranges_.push_back(to_insert); + blocks_ += (to_insert.second - to_insert.first); + } + + void Clear() { + blocks_ = 0; + ranges_.clear(); + } + + using RangeSet::Overlaps; + bool Overlaps(size_t start, size_t len) const { + RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } }); + return Overlaps(rs); + } + + // Compute the block range the file occupies, and insert that range. + void Insert(size_t start, size_t len) { + Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 }; + Insert(to_insert); + } + + // Given an offset of the file, checks if the corresponding block (by considering the file as + // 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset + // within this SortedRangeSet. + // + // For example, the 4106-th byte of a file is from block 1, assuming a block size of 4096-byte. + // The mapped offset within a SortedRangeSet("1-9 15-19") is 10. + // + // An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th + // item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10 + // + 10) in a range represented by this SortedRangeSet. + size_t GetOffsetInRangeSet(size_t old_offset) const { + size_t old_block_start = old_offset / kBlockSize; + size_t new_block_start = 0; + for (const auto& range : ranges_) { + // Find the index of old_block_start. + if (old_block_start >= range.second) { + new_block_start += (range.second - range.first); + } else if (old_block_start >= range.first) { + new_block_start += (old_block_start - range.first); + return (new_block_start * kBlockSize + old_offset % kBlockSize); + } else { + CHECK(false) << "block_start " << old_block_start + << " is missing between two ranges: " << this->ToString(); + return 0; + } + } + CHECK(false) << "block_start " << old_block_start + << " exceeds the limit of current RangeSet: " << this->ToString(); + return 0; + } +}; \ No newline at end of file diff --git a/print_sha1.h b/print_sha1.h deleted file mode 100644 index d0c18b355..000000000 --- a/print_sha1.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef RECOVERY_PRINT_SHA1_H -#define RECOVERY_PRINT_SHA1_H - -#include -#include - -#include - -static std::string print_sha1(const uint8_t* sha1, size_t len) { - const char* hex = "0123456789abcdef"; - std::string result = ""; - for (size_t i = 0; i < len; ++i) { - result.push_back(hex[(sha1[i]>>4) & 0xf]); - result.push_back(hex[sha1[i] & 0xf]); - } - return result; -} - -[[maybe_unused]] static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { - return print_sha1(sha1, SHA_DIGEST_LENGTH); -} - -[[maybe_unused]] static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { - return print_sha1(sha1, 4); -} - -[[maybe_unused]] static std::string print_hex(const uint8_t* bytes, size_t len) { - return print_sha1(bytes, len); -} - -#endif // RECOVERY_PRINT_SHA1_H diff --git a/rangeset.h b/rangeset.h deleted file mode 100644 index f224a08be..000000000 --- a/rangeset.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include -#include - -#include -#include -#include -#include - -using Range = std::pair; - -class RangeSet { - public: - RangeSet() : blocks_(0) {} - - explicit RangeSet(std::vector&& pairs) { - CHECK_NE(pairs.size(), static_cast(0)) << "Invalid number of tokens"; - - // Sanity check the input. - size_t result = 0; - for (const auto& range : pairs) { - CHECK_LT(range.first, range.second) - << "Empty or negative range: " << range.first << ", " << range.second; - size_t sz = range.second - range.first; - CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow"; - result += sz; - } - - ranges_ = pairs; - blocks_ = result; - } - - static RangeSet Parse(const std::string& range_text) { - std::vector pieces = android::base::Split(range_text, ","); - CHECK_GE(pieces.size(), static_cast(3)) << "Invalid range text: " << range_text; - - size_t num; - CHECK(android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) - << "Failed to parse the number of tokens: " << range_text; - - CHECK_NE(num, static_cast(0)) << "Invalid number of tokens: " << range_text; - CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; - CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; - - std::vector pairs; - for (size_t i = 0; i < num; i += 2) { - size_t first; - CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX))); - size_t second; - CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))); - - pairs.emplace_back(first, second); - } - - return RangeSet(std::move(pairs)); - } - - std::string ToString() const { - if (ranges_.empty()) { - return ""; - } - std::string result = std::to_string(ranges_.size() * 2); - for (const auto& r : ranges_) { - result += android::base::StringPrintf(",%zu,%zu", r.first, r.second); - } - - return result; - } - - // Get the block number for the i-th (starting from 0) block in the RangeSet. - size_t GetBlockNumber(size_t idx) const { - CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")"; - - for (const auto& range : ranges_) { - if (idx < range.second - range.first) { - return range.first + idx; - } - idx -= (range.second - range.first); - } - - CHECK(false) << "Failed to find block number for index " << idx; - return 0; // Unreachable, but to make compiler happy. - } - - // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" - // and "5,7" are not overlapped. - bool Overlaps(const RangeSet& other) const { - for (const auto& range : ranges_) { - size_t start = range.first; - size_t end = range.second; - for (const auto& other_range : other.ranges_) { - size_t other_start = other_range.first; - size_t other_end = other_range.second; - // [start, end) vs [other_start, other_end) - if (!(other_start >= end || start >= other_end)) { - return true; - } - } - } - return false; - } - - // size() gives the number of Range's in this RangeSet. - size_t size() const { - return ranges_.size(); - } - - // blocks() gives the number of all blocks in this RangeSet. - size_t blocks() const { - return blocks_; - } - - // We provide const iterators only. - std::vector::const_iterator cbegin() const { - return ranges_.cbegin(); - } - - std::vector::const_iterator cend() const { - return ranges_.cend(); - } - - // Need to provide begin()/end() since range-based loop expects begin()/end(). - std::vector::const_iterator begin() const { - return ranges_.cbegin(); - } - - std::vector::const_iterator end() const { - return ranges_.cend(); - } - - // Reverse const iterators for MoveRange(). - std::vector::const_reverse_iterator crbegin() const { - return ranges_.crbegin(); - } - - std::vector::const_reverse_iterator crend() const { - return ranges_.crend(); - } - - const Range& operator[](size_t i) const { - return ranges_[i]; - } - - bool operator==(const RangeSet& other) const { - // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5". - return (ranges_ == other.ranges_); - } - - bool operator!=(const RangeSet& other) const { - return ranges_ != other.ranges_; - } - - protected: - // Actual limit for each value and the total number are both INT_MAX. - std::vector ranges_; - size_t blocks_; -}; - -static constexpr size_t kBlockSize = 4096; - -// The class is a sorted version of a RangeSet; and it's useful in imgdiff to split the input -// files when we're handling large zip files. Specifically, we can treat the input file as a -// continuous RangeSet (i.e. RangeSet("0-99") for a 100 blocks file); and break it down into -// several smaller chunks based on the zip entries. - -// For example, [source: 0-99] can be split into -// [split_src1: 10-29]; [split_src2: 40-49, 60-69]; [split_src3: 70-89] -// Here "10-29" simply means block 10th to block 29th with respect to the original input file. -// Also, note that the split sources should be mutual exclusive, but they don't need to cover -// every block in the original source. -class SortedRangeSet : public RangeSet { - public: - SortedRangeSet() {} - - // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. - explicit SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { - std::sort(ranges_.begin(), ranges_.end()); - } - - void Insert(const Range& to_insert) { - SortedRangeSet rs({ to_insert }); - Insert(rs); - } - - // Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges. - void Insert(const SortedRangeSet& rs) { - if (rs.size() == 0) { - return; - } - // Merge and sort the two RangeSets. - std::vector temp = std::move(ranges_); - std::copy(rs.begin(), rs.end(), std::back_inserter(temp)); - std::sort(temp.begin(), temp.end()); - - Clear(); - // Trim overlaps and insert the result back to ranges_. - Range to_insert = temp.front(); - for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) { - if (it->first <= to_insert.second) { - to_insert.second = std::max(to_insert.second, it->second); - } else { - ranges_.push_back(to_insert); - blocks_ += (to_insert.second - to_insert.first); - to_insert = *it; - } - } - ranges_.push_back(to_insert); - blocks_ += (to_insert.second - to_insert.first); - } - - void Clear() { - blocks_ = 0; - ranges_.clear(); - } - - using RangeSet::Overlaps; - bool Overlaps(size_t start, size_t len) const { - RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } }); - return Overlaps(rs); - } - - // Compute the block range the file occupies, and insert that range. - void Insert(size_t start, size_t len) { - Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 }; - Insert(to_insert); - } - - // Given an offset of the file, checks if the corresponding block (by considering the file as - // 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset - // within this SortedRangeSet. - // - // For example, the 4106-th byte of a file is from block 1, assuming a block size of 4096-byte. - // The mapped offset within a SortedRangeSet("1-9 15-19") is 10. - // - // An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th - // item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10 - // + 10) in a range represented by this SortedRangeSet. - size_t GetOffsetInRangeSet(size_t old_offset) const { - size_t old_block_start = old_offset / kBlockSize; - size_t new_block_start = 0; - for (const auto& range : ranges_) { - // Find the index of old_block_start. - if (old_block_start >= range.second) { - new_block_start += (range.second - range.first); - } else if (old_block_start >= range.first) { - new_block_start += (old_block_start - range.first); - return (new_block_start * kBlockSize + old_offset % kBlockSize); - } else { - CHECK(false) << "block_start " << old_block_start - << " is missing between two ranges: " << this->ToString(); - return 0; - } - } - CHECK(false) << "block_start " << old_block_start - << " exceeds the limit of current RangeSet: " << this->ToString(); - return 0; - } -}; \ No newline at end of file diff --git a/tests/Android.mk b/tests/Android.mk index 31c7de177..b0f71a832 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -201,6 +201,7 @@ LOCAL_SRC_FILES := \ LOCAL_STATIC_LIBRARIES := \ libimgdiff \ libimgpatch \ + libotautil \ libbsdiff \ libbspatch \ libziparchive \ diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 42542898b..15ec08fe7 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -35,7 +35,7 @@ #include "applypatch/applypatch.h" #include "applypatch/applypatch_modes.h" #include "common/test_constants.h" -#include "print_sha1.h" +#include "otautil/print_sha1.h" static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { ASSERT_NE(nullptr, sha1); diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index e35870dc7..e6aec4ad6 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -41,7 +41,7 @@ #include "edify/expr.h" #include "otautil/SysUtil.h" #include "otautil/error_code.h" -#include "print_sha1.h" +#include "otautil/print_sha1.h" #include "updater/blockimg.h" #include "updater/install.h" #include "updater/updater.h" diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp index 15bcec855..b3ed99215 100644 --- a/tests/unit/rangeset_test.cpp +++ b/tests/unit/rangeset_test.cpp @@ -21,7 +21,7 @@ #include -#include "rangeset.h" +#include "otautil/rangeset.h" TEST(RangeSetTest, Parse_smoke) { RangeSet rs = RangeSet::Parse("2,1,10"); @@ -156,4 +156,4 @@ TEST(SortedRangeSetTest, file_range) { ASSERT_EQ(static_cast(40970), rs.GetOffsetInRangeSet(4096 * 16 + 10)); // block#10 not in range. ASSERT_EXIT(rs.GetOffsetInRangeSet(40970), ::testing::KilledBySignal(SIGABRT), ""); -} \ No newline at end of file +} diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 0f8364441..ce3cea4f3 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -52,8 +52,8 @@ #include "edify/expr.h" #include "otafault/ota_io.h" #include "otautil/error_code.h" -#include "print_sha1.h" -#include "rangeset.h" +#include "otautil/print_sha1.h" +#include "otautil/rangeset.h" #include "updater/install.h" #include "updater/updater.h" diff --git a/updater/install.cpp b/updater/install.cpp index 01210f51c..9425d1872 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include "edify/expr.h" @@ -61,8 +62,7 @@ #include "otafault/ota_io.h" #include "otautil/DirUtil.h" #include "otautil/error_code.h" -#include "print_sha1.h" -#include "tune2fs.h" +#include "otautil/print_sha1.h" #include "updater/updater.h" // Send over the buffer to recovery though the command pipe. diff --git a/verifier.cpp b/verifier.cpp index 18437fb7a..283e04300 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -32,7 +32,7 @@ #include #include "asn1_decoder.h" -#include "print_sha1.h" +#include "otautil/print_sha1.h" static constexpr size_t MiB = 1024 * 1024; -- cgit v1.2.3 From 44fc7b1e7925da585ae9aa36c127575566933467 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 11 Oct 2017 11:32:46 -0700 Subject: applypatch: Use shared lib for libbz. We have shared lib target for libbz now (and libbz.so is already on device because of /system/bin/bzip2). Test: m applypatch Change-Id: I5fe2468a8d535840245f081a92d436240dddbf6b --- applypatch/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applypatch/Android.mk b/applypatch/Android.mk index c9ce34bc3..8fac2ffed 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -120,10 +120,10 @@ LOCAL_STATIC_LIBRARIES := \ libedify \ libotafault \ libotautil \ - libbspatch \ - libbz + libbspatch LOCAL_SHARED_LIBRARIES := \ libbase \ + libbz \ libcrypto \ liblog \ libz \ -- cgit v1.2.3 From c8686b493cf7d31308ce3e36d5a1f13972f255a5 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Wed, 11 Oct 2017 11:47:54 -0700 Subject: Add missing includes. Test: mma Bug: None Change-Id: I8b588dfbc00ef67557622d44700b331c7a31bde3 --- minui/events.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/minui/events.cpp b/minui/events.cpp index 24c2a8277..2894c3b6b 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include #include -- cgit v1.2.3 From b9bffdffb944663c61fdbebcdedcf9b87fd2450e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 11 Oct 2017 12:14:38 -0700 Subject: otautil: #include for TEMP_FAILURE_RETRY. Test: mmma bootable/recovery Change-Id: I5959303528c6f704f10ce153f6fcb2054ce35b1e --- otautil/SysUtil.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/otautil/SysUtil.cpp b/otautil/SysUtil.cpp index 0655c4778..48336ad07 100644 --- a/otautil/SysUtil.cpp +++ b/otautil/SysUtil.cpp @@ -16,6 +16,7 @@ #include "otautil/SysUtil.h" +#include // TEMP_FAILURE_RETRY #include #include // SIZE_MAX #include -- cgit v1.2.3 From f0ca4f8f2d8feabc2682c08ded5937427752a78e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 3 Oct 2017 17:21:11 -0700 Subject: otafault: Add the missing #include . Test: mmma bootable/recovery Change-Id: If7610ba55a2d2dd4257ff38c44d252c9c6bbfe40 --- otafault/ota_io.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp index 1308973a5..63ef18e26 100644 --- a/otafault/ota_io.cpp +++ b/otafault/ota_io.cpp @@ -26,6 +26,7 @@ #include #include +#include #include -- cgit v1.2.3 From 526699fc679ecc03461c8534f640edbcb621cf26 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 11 Oct 2017 15:59:28 -0700 Subject: applypatch: Remove the Makefile for building imgdiff in Chrome OS. The former user system/update_engine has dropped the support for imgdiff (https://android-review.googlesource.com/c/platform/system/update_engine/+/400427/). Test: N/A Change-Id: Ia8f770dc51800b45064c4affc3514cf513877edc --- applypatch/Makefile | 33 --------------------------------- applypatch/libimgpatch.pc | 6 ------ 2 files changed, 39 deletions(-) delete mode 100644 applypatch/Makefile delete mode 100644 applypatch/libimgpatch.pc diff --git a/applypatch/Makefile b/applypatch/Makefile deleted file mode 100644 index fb4984303..000000000 --- a/applypatch/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This file is for building imgdiff in Chrome OS. - -CPPFLAGS += -iquote.. -Iinclude -CXXFLAGS += -std=c++11 -O3 -Wall -Werror -LDLIBS += -lbz2 -lz - -.PHONY: all clean - -all: imgdiff libimgpatch.a - -clean: - rm -f *.o imgdiff libimgpatch.a - -imgdiff: imgdiff.o bsdiff.o utils.o - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDLIBS) -o $@ $^ - -libimgpatch.a utils.o: CXXFLAGS += -fPIC -libimgpatch.a: imgpatch.o bspatch.o utils.o - ${AR} rcs $@ $^ diff --git a/applypatch/libimgpatch.pc b/applypatch/libimgpatch.pc deleted file mode 100644 index e5002934f..000000000 --- a/applypatch/libimgpatch.pc +++ /dev/null @@ -1,6 +0,0 @@ -# This file is for libimgpatch in Chrome OS. - -Name: libimgpatch -Description: Apply imgdiff patch -Version: 0.0.1 -Libs: -limgpatch -lbz2 -lz -- cgit v1.2.3 From 582b67850fc9ea9819ea28047b341d4e31c89ca7 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 5 Oct 2017 09:06:21 -0700 Subject: applypatch: Move to Soong. Test: mmma bootable/recovery Change-Id: Ie163aff1c4c2b3b15bb705825779ada6bc38ad67 --- Android.bp | 1 + Android.mk | 1 - applypatch/Android.bp | 195 +++++++++++++++++++++++++++++++++++++++++++++++++ applypatch/Android.mk | 196 -------------------------------------------------- 4 files changed, 196 insertions(+), 197 deletions(-) create mode 100644 applypatch/Android.bp delete mode 100644 applypatch/Android.mk diff --git a/Android.bp b/Android.bp index 4c66c427e..22407e0e2 100644 --- a/Android.bp +++ b/Android.bp @@ -1,4 +1,5 @@ subdirs = [ + "applypatch", "bootloader_message", "edify", "otafault", diff --git a/Android.mk b/Android.mk index 5fe37fd60..04b1625bf 100644 --- a/Android.mk +++ b/Android.mk @@ -260,7 +260,6 @@ LOCAL_MODULE := librecovery_ui_vr include $(BUILD_STATIC_LIBRARY) include \ - $(LOCAL_PATH)/applypatch/Android.mk \ $(LOCAL_PATH)/boot_control/Android.mk \ $(LOCAL_PATH)/minadbd/Android.mk \ $(LOCAL_PATH)/minui/Android.mk \ diff --git a/applypatch/Android.bp b/applypatch/Android.bp new file mode 100644 index 000000000..db307df28 --- /dev/null +++ b/applypatch/Android.bp @@ -0,0 +1,195 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_defaults { + name: "applypatch_defaults", + + cflags: [ + "-D_FILE_OFFSET_BITS=64", + "-DZLIB_CONST", + "-Wall", + "-Werror", + ], + + local_include_dirs: [ + "include", + ], +} + +cc_library_static { + name: "libapplypatch", + + defaults: [ + "applypatch_defaults", + ], + + srcs: [ + "applypatch.cpp", + "bspatch.cpp", + "freecache.cpp", + "imgpatch.cpp", + ], + + export_include_dirs: [ + "include", + ], + + static_libs: [ + "libbase", + "libbspatch", + "libbz", + "libcrypto", + "libedify", + "libotafault", + "libotautil", + "libz", + ], +} + +cc_library_static { + name: "libapplypatch_modes", + + defaults: [ + "applypatch_defaults", + ], + + srcs: [ + "applypatch_modes.cpp", + ], + + static_libs: [ + "libapplypatch", + "libbase", + "libcrypto", + "libedify", + "libotautil", + ], +} + +cc_binary { + name: "applypatch", + + defaults: [ + "applypatch_defaults", + ], + + srcs: [ + "applypatch_main.cpp", + ], + + static_libs: [ + "libapplypatch_modes", + "libapplypatch", + "libedify", + "libotafault", + "libotautil", + "libbspatch", + ], + + shared_libs: [ + "libbase", + "libbz", + "libcrypto", + "liblog", + "libz", + "libziparchive", + ], +} + +cc_library_static { + name: "libimgdiff", + + host_supported: true, + + defaults: [ + "applypatch_defaults", + ], + + srcs: [ + "imgdiff.cpp", + ], + + export_include_dirs: [ + "include", + ], + + static_libs: [ + "libbase", + "libbsdiff", + "libdivsufsort", + "libdivsufsort64", + "liblog", + "libotautil", + "libutils", + "libz", + "libziparchive", + ], +} + +cc_binary_host { + name: "imgdiff", + + srcs: [ + "imgdiff_main.cpp", + ], + + defaults: [ + "applypatch_defaults", + ], + + static_libs: [ + "libimgdiff", + "libbsdiff", + "libdivsufsort", + "libdivsufsort64", + "libziparchive", + "libbase", + "libutils", + "liblog", + "libbz", + "libz", + ], +} + +cc_library_static { + name: "libimgpatch", + + // The host module is for recovery_host_test (Linux only). + host_supported: true, + + defaults: [ + "applypatch_defaults", + ], + + srcs: [ + "bspatch.cpp", + "imgpatch.cpp", + ], + + static_libs: [ + "libbase", + "libbspatch", + "libbz", + "libcrypto", + "libedify", + "libotautil", + "libz", + ], + + target: { + darwin: { + enabled: false, + }, + }, +} diff --git a/applypatch/Android.mk b/applypatch/Android.mk deleted file mode 100644 index 8fac2ffed..000000000 --- a/applypatch/Android.mk +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (C) 2008 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -# libapplypatch (static library) -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - applypatch.cpp \ - bspatch.cpp \ - freecache.cpp \ - imgpatch.cpp -LOCAL_MODULE := libapplypatch -LOCAL_MODULE_TAGS := eng -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := \ - $(LOCAL_PATH)/include -LOCAL_STATIC_LIBRARIES := \ - libedify \ - libotafault \ - libotautil \ - libbase \ - libcrypto \ - libbspatch \ - libbz \ - libz -LOCAL_CFLAGS := \ - -DZLIB_CONST \ - -Wall \ - -Werror -include $(BUILD_STATIC_LIBRARY) - -# libimgpatch (static library) -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - bspatch.cpp \ - imgpatch.cpp -LOCAL_MODULE := libimgpatch -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := \ - $(LOCAL_PATH)/include -LOCAL_STATIC_LIBRARIES := \ - libedify \ - libotautil \ - libcrypto \ - libbspatch \ - libbase \ - libbz \ - libz -LOCAL_CFLAGS := \ - -DZLIB_CONST \ - -Wall \ - -Werror -include $(BUILD_STATIC_LIBRARY) - -# libimgpatch (host static library) -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - bspatch.cpp \ - imgpatch.cpp -LOCAL_MODULE := libimgpatch -LOCAL_MODULE_HOST_OS := linux -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := \ - $(LOCAL_PATH)/include -LOCAL_STATIC_LIBRARIES := \ - libedify \ - libotautil \ - libcrypto \ - libbspatch \ - libbase \ - libbz \ - libz -LOCAL_CFLAGS := \ - -DZLIB_CONST \ - -Wall \ - -Werror -include $(BUILD_HOST_STATIC_LIBRARY) - -# libapplypatch_modes (static library) -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - applypatch_modes.cpp -LOCAL_MODULE := libapplypatch_modes -LOCAL_STATIC_LIBRARIES := \ - libapplypatch \ - libotautil \ - libbase \ - libedify \ - libcrypto -LOCAL_CFLAGS := -Wall -Werror -include $(BUILD_STATIC_LIBRARY) - -# applypatch (target executable) -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := applypatch_main.cpp -LOCAL_MODULE := applypatch -LOCAL_STATIC_LIBRARIES := \ - libapplypatch_modes \ - libapplypatch \ - libedify \ - libotafault \ - libotautil \ - libbspatch -LOCAL_SHARED_LIBRARIES := \ - libbase \ - libbz \ - libcrypto \ - liblog \ - libz \ - libziparchive -LOCAL_CFLAGS := -Wall -Werror -include $(BUILD_EXECUTABLE) - -libimgdiff_src_files := imgdiff.cpp - -# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64. -libimgdiff_cflags := \ - -Wall \ - -Werror \ - -D_FILE_OFFSET_BITS=64 \ - -DZLIB_CONST - -libimgdiff_static_libraries := \ - libotautil \ - libbsdiff \ - libdivsufsort \ - libdivsufsort64 \ - libziparchive \ - libutils \ - liblog \ - libbase \ - libz - -# libimgdiff (static library) -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - $(libimgdiff_src_files) -LOCAL_MODULE := libimgdiff -LOCAL_CFLAGS := \ - $(libimgdiff_cflags) -LOCAL_STATIC_LIBRARIES := \ - $(libimgdiff_static_libraries) -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include -include $(BUILD_STATIC_LIBRARY) - -# libimgdiff (host static library) -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - $(libimgdiff_src_files) -LOCAL_MODULE := libimgdiff -LOCAL_CFLAGS := \ - $(libimgdiff_cflags) -LOCAL_STATIC_LIBRARIES := \ - $(libimgdiff_static_libraries) -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include -include $(BUILD_HOST_STATIC_LIBRARY) - -# imgdiff (host static executable) -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := imgdiff_main.cpp -LOCAL_MODULE := imgdiff -LOCAL_CFLAGS := -Wall -Werror -LOCAL_STATIC_LIBRARIES := \ - libimgdiff \ - $(libimgdiff_static_libraries) \ - libbz -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include -include $(BUILD_HOST_EXECUTABLE) -- cgit v1.2.3 From 99f0d9e52bdbe314d77300f883d66e4470d4a5ee Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 13 Oct 2016 12:46:38 -0700 Subject: Drop -Wno-unused-parameter. The only one left is libedify. Will handle that in a separate CL. Test: mmma bootable/recovery Change-Id: I732a5f85229da90fd767bee2e46c5c95f529c396 --- Android.mk | 6 ++---- boot_control/Android.mk | 3 +-- minadbd/Android.mk | 17 ++++++++++++++++- minadbd/minadbd_services.cpp | 30 +++++++++++++++--------------- minui/Android.mk | 5 +++++ recovery.cpp | 21 ++++++++++----------- stub_ui.h | 22 +++++++++++----------- updater/Android.mk | 2 -- vr_ui.cpp | 2 +- wear_ui.cpp | 3 +-- 10 files changed, 62 insertions(+), 49 deletions(-) diff --git a/Android.mk b/Android.mk index 5fe37fd60..132c6fe4a 100644 --- a/Android.mk +++ b/Android.mk @@ -94,7 +94,7 @@ endif endif LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) -LOCAL_CFLAGS += -Wall -Wno-unused-parameter -Werror +LOCAL_CFLAGS += -Wall -Werror ifneq ($(TARGET_RECOVERY_UI_MARGIN_HEIGHT),) LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=$(TARGET_RECOVERY_UI_MARGIN_HEIGHT) @@ -173,9 +173,7 @@ LOCAL_STATIC_LIBRARIES := \ libcutils \ libutils \ liblog \ - libselinux \ - libm \ - libc + libselinux LOCAL_HAL_STATIC_LIBRARIES := libhealthd diff --git a/boot_control/Android.mk b/boot_control/Android.mk index 27e3d9765..9814d7122 100644 --- a/boot_control/Android.mk +++ b/boot_control/Android.mk @@ -24,8 +24,7 @@ LOCAL_CFLAGS := \ -D_FILE_OFFSET_BITS=64 \ -Werror \ -Wall \ - -Wextra \ - -Wno-unused-parameter + -Wextra LOCAL_SHARED_LIBRARIES := liblog LOCAL_STATIC_LIBRARIES := libbootloader_message libfs_mgr libbase LOCAL_POST_INSTALL_CMD := \ diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 8d86fd653..803171d99 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -1,13 +1,26 @@ # Copyright 2005 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. LOCAL_PATH:= $(call my-dir) minadbd_cflags := \ -Wall -Werror \ - -Wno-unused-parameter \ -Wno-missing-field-initializers \ -DADB_HOST=0 \ +# libadbd (static library) +# =============================== include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -24,6 +37,8 @@ LOCAL_STATIC_LIBRARIES := libcrypto libbase include $(BUILD_STATIC_LIBRARY) +# minadbd_test (native test) +# =============================== include $(CLEAR_VARS) LOCAL_MODULE := minadbd_test diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 61c06cc0a..9f0f1f87d 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -58,20 +58,20 @@ static int create_service_thread(void (*func)(int, const std::string&), const st return s[0]; } -int service_to_fd(const char* name, const atransport* transport) { - int ret = -1; +int service_to_fd(const char* name, const atransport* /* transport */) { + int ret = -1; - if (!strncmp(name, "sideload:", 9)) { - // this exit status causes recovery to print a special error - // message saying to use a newer adb (that supports - // sideload-host). - exit(3); - } else if (!strncmp(name, "sideload-host:", 14)) { - std::string arg(name + 14); - ret = create_service_thread(sideload_host_service, arg); - } - if (ret >= 0) { - close_on_exec(ret); - } - return ret; + if (!strncmp(name, "sideload:", 9)) { + // this exit status causes recovery to print a special error + // message saying to use a newer adb (that supports + // sideload-host). + exit(3); + } else if (!strncmp(name, "sideload-host:", 14)) { + std::string arg(name + 14); + ret = create_service_thread(sideload_host_service, arg); + } + if (ret >= 0) { + close_on_exec(ret); + } + return ret; } diff --git a/minui/Android.mk b/minui/Android.mk index 9a217a48f..ae1552b1b 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -13,6 +13,9 @@ # limitations under the License. LOCAL_PATH := $(call my-dir) + +# libminui (static library) +# =============================== include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -66,6 +69,8 @@ endif include $(BUILD_STATIC_LIBRARY) +# libminui (shared library) +# =============================== # Used by OEMs for factory test images. include $(CLEAR_VARS) LOCAL_MODULE := libminui diff --git a/recovery.cpp b/recovery.cpp index 243ee4af3..a89916337 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1214,9 +1214,8 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { } } -static void -print_property(const char *key, const char *name, void *cookie) { - printf("%s=%s\n", key, name); +static void print_property(const char* key, const char* name, void* /* cookie */) { + printf("%s=%s\n", key, name); } static std::string load_locale_from_cache() { @@ -1250,14 +1249,14 @@ void ui_print(const char* format, ...) { static constexpr char log_characters[] = "VDIWEF"; -void UiLogger(android::base::LogId id, android::base::LogSeverity severity, - const char* tag, const char* file, unsigned int line, - const char* message) { - if (severity >= android::base::ERROR && ui != nullptr) { - ui->Print("E:%s\n", message); - } else { - fprintf(stdout, "%c:%s\n", log_characters[severity], message); - } +void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity, + const char* /* tag */, const char* /* file */, unsigned int /* line */, + const char* message) { + if (severity >= android::base::ERROR && ui != nullptr) { + ui->Print("E:%s\n", message); + } else { + fprintf(stdout, "%c:%s\n", log_characters[severity], message); + } } static bool is_battery_ok() { diff --git a/stub_ui.h b/stub_ui.h index 85dbcfd89..1f6b29acb 100644 --- a/stub_ui.h +++ b/stub_ui.h @@ -24,18 +24,18 @@ class StubRecoveryUI : public RecoveryUI { public: StubRecoveryUI() = default; - void SetBackground(Icon icon) override {} - void SetSystemUpdateText(bool security_update) override {} + void SetBackground(Icon /* icon */) override {} + void SetSystemUpdateText(bool /* security_update */) override {} // progress indicator - void SetProgressType(ProgressType type) override {} - void ShowProgress(float portion, float seconds) override {} - void SetProgress(float fraction) override {} + void SetProgressType(ProgressType /* type */) override {} + void ShowProgress(float /* portion */, float /* seconds */) override {} + void SetProgress(float /* fraction */) override {} - void SetStage(int current, int max) override {} + void SetStage(int /* current */, int /* max */) override {} // text log - void ShowText(bool visible) override {} + void ShowText(bool /* visible */) override {} bool IsTextVisible() override { return false; } @@ -50,12 +50,12 @@ class StubRecoveryUI : public RecoveryUI { vprintf(fmt, ap); va_end(ap); } - void PrintOnScreenOnly(const char* fmt, ...) override {} - void ShowFile(const char* filename) override {} + void PrintOnScreenOnly(const char* /* fmt */, ...) override {} + void ShowFile(const char* /* filename */) override {} // menu display - void StartMenu(const char* const* headers, const char* const* items, - int initial_selection) override {} + void StartMenu(const char* const* /* headers */, const char* const* /* items */, + int /* initial_selection */) override {} int SelectMenu(int sel) override { return sel; } diff --git a/updater/Android.mk b/updater/Android.mk index 12181602f..6f334ee18 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -67,7 +67,6 @@ LOCAL_C_INCLUDES := \ LOCAL_CFLAGS := \ -Wall \ - -Wno-unused-parameter \ -Werror LOCAL_EXPORT_C_INCLUDE_DIRS := \ @@ -93,7 +92,6 @@ LOCAL_C_INCLUDES := \ LOCAL_CFLAGS := \ -Wall \ - -Wno-unused-parameter \ -Werror LOCAL_STATIC_LIBRARIES := \ diff --git a/vr_ui.cpp b/vr_ui.cpp index 07cc9da59..a58c99efd 100644 --- a/vr_ui.cpp +++ b/vr_ui.cpp @@ -53,7 +53,7 @@ int VrRecoveryUI::DrawHorizontalRule(int y) const { return y + 4; } -void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { +void VrRecoveryUI::DrawHighlightBar(int /* x */, int y, int /* width */, int height) const { gr_fill(kMarginWidth + kStereoOffset, y, ScreenWidth() - kMarginWidth + kStereoOffset, y + height); gr_fill(ScreenWidth() + kMarginWidth - kStereoOffset, y, gr_fb_width() - kMarginWidth - kStereoOffset, y + height); diff --git a/wear_ui.cpp b/wear_ui.cpp index e2ee48804..ca6b1b102 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -150,8 +150,7 @@ void WearRecoveryUI::update_progress_locked() { gr_flip(); } -void WearRecoveryUI::SetStage(int current, int max) { -} +void WearRecoveryUI::SetStage(int /* current */, int /* max */) {} void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* items, int initial_selection) { -- cgit v1.2.3 From 4945407f5bdb4a59e7f60b9d62dc41b18609ccaf Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Thu, 12 Oct 2017 10:45:04 -0700 Subject: Fix owner email address. Test: normal build. Bug: 67718313 Change-Id: Ia3144c634647f7a457810370a9f7275c19a8aa76 --- OWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OWNERS b/OWNERS index 09754c650..b3f11dcd6 100644 --- a/OWNERS +++ b/OWNERS @@ -1,3 +1,3 @@ -enh+aosp-gerrit@google.com +enh@google.com tbao@google.com xunchang@google.com -- cgit v1.2.3 From 45685820029fb191fe8509418df91a049227ea3a Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 13 Oct 2017 14:54:12 -0700 Subject: otautil: Move RangeSet implementation into rangeset.cpp. Since it has grown much larger, users of the header shouldn't compile and carry their full copies. Also add missing header includes in imgdiff.cpp and imgdiff_test.cpp. Test: mmma bootable/recovery Test: recovery_unit_test; recovery_component_test; recovery_host_test Change-Id: I88ca54171765e5606ab0d61580fbc1ada578fd7d --- applypatch/Android.bp | 1 + applypatch/imgdiff.cpp | 2 + otautil/Android.bp | 1 + otautil/include/otautil/rangeset.h | 166 +++--------------------------- otautil/rangeset.cpp | 200 +++++++++++++++++++++++++++++++++++++ tests/component/imgdiff_test.cpp | 1 + 6 files changed, 220 insertions(+), 151 deletions(-) create mode 100644 otautil/rangeset.cpp diff --git a/applypatch/Android.bp b/applypatch/Android.bp index db307df28..922f67abf 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -150,6 +150,7 @@ cc_binary_host { static_libs: [ "libimgdiff", + "libotautil", "libbsdiff", "libdivsufsort", "libdivsufsort64", diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 3a61a7d0d..69ad75f37 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -160,6 +160,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/otautil/Android.bp b/otautil/Android.bp index 659fefada..5efb12d60 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -21,6 +21,7 @@ cc_library_static { "SysUtil.cpp", "DirUtil.cpp", "ThermalUtil.cpp", + "rangeset.cpp", ], static_libs: [ diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h index f224a08be..c4234d181 100644 --- a/otautil/include/otautil/rangeset.h +++ b/otautil/include/otautil/rangeset.h @@ -22,103 +22,24 @@ #include #include -#include -#include -#include -#include - using Range = std::pair; class RangeSet { public: RangeSet() : blocks_(0) {} - explicit RangeSet(std::vector&& pairs) { - CHECK_NE(pairs.size(), static_cast(0)) << "Invalid number of tokens"; - - // Sanity check the input. - size_t result = 0; - for (const auto& range : pairs) { - CHECK_LT(range.first, range.second) - << "Empty or negative range: " << range.first << ", " << range.second; - size_t sz = range.second - range.first; - CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow"; - result += sz; - } - - ranges_ = pairs; - blocks_ = result; - } - - static RangeSet Parse(const std::string& range_text) { - std::vector pieces = android::base::Split(range_text, ","); - CHECK_GE(pieces.size(), static_cast(3)) << "Invalid range text: " << range_text; - - size_t num; - CHECK(android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) - << "Failed to parse the number of tokens: " << range_text; - - CHECK_NE(num, static_cast(0)) << "Invalid number of tokens: " << range_text; - CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; - CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; - - std::vector pairs; - for (size_t i = 0; i < num; i += 2) { - size_t first; - CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX))); - size_t second; - CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))); + explicit RangeSet(std::vector&& pairs); - pairs.emplace_back(first, second); - } + static RangeSet Parse(const std::string& range_text); - return RangeSet(std::move(pairs)); - } - - std::string ToString() const { - if (ranges_.empty()) { - return ""; - } - std::string result = std::to_string(ranges_.size() * 2); - for (const auto& r : ranges_) { - result += android::base::StringPrintf(",%zu,%zu", r.first, r.second); - } - - return result; - } + std::string ToString() const; // Get the block number for the i-th (starting from 0) block in the RangeSet. - size_t GetBlockNumber(size_t idx) const { - CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")"; - - for (const auto& range : ranges_) { - if (idx < range.second - range.first) { - return range.first + idx; - } - idx -= (range.second - range.first); - } - - CHECK(false) << "Failed to find block number for index " << idx; - return 0; // Unreachable, but to make compiler happy. - } + size_t GetBlockNumber(size_t idx) const; // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" // and "5,7" are not overlapped. - bool Overlaps(const RangeSet& other) const { - for (const auto& range : ranges_) { - size_t start = range.first; - size_t end = range.second; - for (const auto& other_range : other.ranges_) { - size_t other_start = other_range.first; - size_t other_end = other_range.second; - // [start, end) vs [other_start, other_end) - if (!(other_start >= end || start >= other_end)) { - return true; - } - } - } - return false; - } + bool Overlaps(const RangeSet& other) const; // size() gives the number of Range's in this RangeSet. size_t size() const { @@ -176,8 +97,6 @@ class RangeSet { size_t blocks_; }; -static constexpr size_t kBlockSize = 4096; - // The class is a sorted version of a RangeSet; and it's useful in imgdiff to split the input // files when we're handling large zip files. Specifically, we can treat the input file as a // continuous RangeSet (i.e. RangeSet("0-99") for a 100 blocks file); and break it down into @@ -193,57 +112,21 @@ class SortedRangeSet : public RangeSet { SortedRangeSet() {} // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. - explicit SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { - std::sort(ranges_.begin(), ranges_.end()); - } + explicit SortedRangeSet(std::vector&& pairs); - void Insert(const Range& to_insert) { - SortedRangeSet rs({ to_insert }); - Insert(rs); - } + void Insert(const Range& to_insert); // Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges. - void Insert(const SortedRangeSet& rs) { - if (rs.size() == 0) { - return; - } - // Merge and sort the two RangeSets. - std::vector temp = std::move(ranges_); - std::copy(rs.begin(), rs.end(), std::back_inserter(temp)); - std::sort(temp.begin(), temp.end()); - - Clear(); - // Trim overlaps and insert the result back to ranges_. - Range to_insert = temp.front(); - for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) { - if (it->first <= to_insert.second) { - to_insert.second = std::max(to_insert.second, it->second); - } else { - ranges_.push_back(to_insert); - blocks_ += (to_insert.second - to_insert.first); - to_insert = *it; - } - } - ranges_.push_back(to_insert); - blocks_ += (to_insert.second - to_insert.first); - } + void Insert(const SortedRangeSet& rs); - void Clear() { - blocks_ = 0; - ranges_.clear(); - } + // Compute the block range the file occupies, and insert that range. + void Insert(size_t start, size_t len); + + void Clear(); using RangeSet::Overlaps; - bool Overlaps(size_t start, size_t len) const { - RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } }); - return Overlaps(rs); - } - // Compute the block range the file occupies, and insert that range. - void Insert(size_t start, size_t len) { - Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 }; - Insert(to_insert); - } + bool Overlaps(size_t start, size_t len) const; // Given an offset of the file, checks if the corresponding block (by considering the file as // 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset @@ -255,24 +138,5 @@ class SortedRangeSet : public RangeSet { // An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th // item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10 // + 10) in a range represented by this SortedRangeSet. - size_t GetOffsetInRangeSet(size_t old_offset) const { - size_t old_block_start = old_offset / kBlockSize; - size_t new_block_start = 0; - for (const auto& range : ranges_) { - // Find the index of old_block_start. - if (old_block_start >= range.second) { - new_block_start += (range.second - range.first); - } else if (old_block_start >= range.first) { - new_block_start += (old_block_start - range.first); - return (new_block_start * kBlockSize + old_offset % kBlockSize); - } else { - CHECK(false) << "block_start " << old_block_start - << " is missing between two ranges: " << this->ToString(); - return 0; - } - } - CHECK(false) << "block_start " << old_block_start - << " exceeds the limit of current RangeSet: " << this->ToString(); - return 0; - } -}; \ No newline at end of file + size_t GetOffsetInRangeSet(size_t old_offset) const; +}; diff --git a/otautil/rangeset.cpp b/otautil/rangeset.cpp new file mode 100644 index 000000000..a121a4efc --- /dev/null +++ b/otautil/rangeset.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otautil/rangeset.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +RangeSet::RangeSet(std::vector&& pairs) { + CHECK_NE(pairs.size(), static_cast(0)) << "Invalid number of tokens"; + + // Sanity check the input. + size_t result = 0; + for (const auto& range : pairs) { + CHECK_LT(range.first, range.second) << "Empty or negative range: " << range.first << ", " + << range.second; + size_t sz = range.second - range.first; + CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow"; + result += sz; + } + + ranges_ = pairs; + blocks_ = result; +} + +RangeSet RangeSet::Parse(const std::string& range_text) { + std::vector pieces = android::base::Split(range_text, ","); + CHECK_GE(pieces.size(), static_cast(3)) << "Invalid range text: " << range_text; + + size_t num; + CHECK(android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) + << "Failed to parse the number of tokens: " << range_text; + + CHECK_NE(num, static_cast(0)) << "Invalid number of tokens: " << range_text; + CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; + CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; + + std::vector pairs; + for (size_t i = 0; i < num; i += 2) { + size_t first; + CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX))); + size_t second; + CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))); + + pairs.emplace_back(first, second); + } + + return RangeSet(std::move(pairs)); +} + +std::string RangeSet::ToString() const { + if (ranges_.empty()) { + return ""; + } + std::string result = std::to_string(ranges_.size() * 2); + for (const auto& r : ranges_) { + result += android::base::StringPrintf(",%zu,%zu", r.first, r.second); + } + + return result; +} + +// Get the block number for the i-th (starting from 0) block in the RangeSet. +size_t RangeSet::GetBlockNumber(size_t idx) const { + CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")"; + + for (const auto& range : ranges_) { + if (idx < range.second - range.first) { + return range.first + idx; + } + idx -= (range.second - range.first); + } + + CHECK(false) << "Failed to find block number for index " << idx; + return 0; // Unreachable, but to make compiler happy. +} + +// RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" +// and "5,7" are not overlapped. +bool RangeSet::Overlaps(const RangeSet& other) const { + for (const auto& range : ranges_) { + size_t start = range.first; + size_t end = range.second; + for (const auto& other_range : other.ranges_) { + size_t other_start = other_range.first; + size_t other_end = other_range.second; + // [start, end) vs [other_start, other_end) + if (!(other_start >= end || start >= other_end)) { + return true; + } + } + } + return false; +} + +static constexpr size_t kBlockSize = 4096; + +// Ranges in the the set should be mutually exclusive; and they're sorted by the start block. +SortedRangeSet::SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { + std::sort(ranges_.begin(), ranges_.end()); +} + +void SortedRangeSet::Insert(const Range& to_insert) { + SortedRangeSet rs({ to_insert }); + Insert(rs); +} + +// Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges. +void SortedRangeSet::Insert(const SortedRangeSet& rs) { + if (rs.size() == 0) { + return; + } + // Merge and sort the two RangeSets. + std::vector temp = std::move(ranges_); + std::copy(rs.begin(), rs.end(), std::back_inserter(temp)); + std::sort(temp.begin(), temp.end()); + + Clear(); + // Trim overlaps and insert the result back to ranges_. + Range to_insert = temp.front(); + for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) { + if (it->first <= to_insert.second) { + to_insert.second = std::max(to_insert.second, it->second); + } else { + ranges_.push_back(to_insert); + blocks_ += (to_insert.second - to_insert.first); + to_insert = *it; + } + } + ranges_.push_back(to_insert); + blocks_ += (to_insert.second - to_insert.first); +} + +// Compute the block range the file occupies, and insert that range. +void SortedRangeSet::Insert(size_t start, size_t len) { + Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 }; + Insert(to_insert); +} + +void SortedRangeSet::Clear() { + blocks_ = 0; + ranges_.clear(); +} + +bool SortedRangeSet::Overlaps(size_t start, size_t len) const { + RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } }); + return Overlaps(rs); +} + +// Given an offset of the file, checks if the corresponding block (by considering the file as +// 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset +// within this SortedRangeSet. +// +// For example, the 4106-th byte of a file is from block 1, assuming a block size of 4096-byte. +// The mapped offset within a SortedRangeSet("1-9 15-19") is 10. +// +// An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th +// item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10 +// + 10) in a range represented by this SortedRangeSet. +size_t SortedRangeSet::GetOffsetInRangeSet(size_t old_offset) const { + size_t old_block_start = old_offset / kBlockSize; + size_t new_block_start = 0; + for (const auto& range : ranges_) { + // Find the index of old_block_start. + if (old_block_start >= range.second) { + new_block_start += (range.second - range.first); + } else if (old_block_start >= range.first) { + new_block_start += (old_block_start - range.first); + return (new_block_start * kBlockSize + old_offset % kBlockSize); + } else { + CHECK(false) << "block_start " << old_block_start + << " is missing between two ranges: " << this->ToString(); + return 0; + } + } + CHECK(false) << "block_start " << old_block_start + << " exceeds the limit of current RangeSet: " << this->ToString(); + return 0; +} diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 161d58d45..bf591dae4 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 245c5225880f763a31e9ac0fd42dee2cc42cfc9d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 18 Oct 2017 11:06:36 -0700 Subject: rotate_logs: Clean up the header includes. Also clang-format rotate_logs.cpp to get consistent style. Test: mmma bootable/recovery Test: recovery_manual_test works on marlin. Change-Id: I1210a346b97bbf0e1a8c3f8e3e73a7c114858ca8 --- rotate_logs.cpp | 120 ++++++++++++++++++++++++++------------------------------ rotate_logs.h | 22 ++++------- 2 files changed, 64 insertions(+), 78 deletions(-) diff --git a/rotate_logs.cpp b/rotate_logs.cpp index fc220215e..da008792c 100644 --- a/rotate_logs.cpp +++ b/rotate_logs.cpp @@ -31,85 +31,77 @@ 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 /* logId */, - char /* prio */, - const char *filename, - const char * /* buf */, size_t len, - void *arg) { - bool* doRotate = static_cast(arg); - if (LAST_KMSG_FILTER.find(filename) != std::string::npos || - LAST_LOG_FILTER.find(filename) != std::string::npos) { - *doRotate = true; - } - return len; +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 logId, - char prio, - const char *filename, - const char *buf, size_t len, - void *arg) { - bool* doRotate = static_cast(arg); - if (!*doRotate) { - return __android_log_pmsg_file_write(logId, prio, filename, buf, 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); + 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(logId, prio, filename, buf, len); - } + 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"; + // filename rotation + if (dot == std::string::npos) { + name += ".1"; + } else { + std::string number = name.substr(dot + 1); + if (!isdigit(number[0])) { + 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); - } + 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(logId, prio, name.c_str(), buf, len); + 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; + // 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()); + 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()); + 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()); + } } diff --git a/rotate_logs.h b/rotate_logs.h index 809c213b6..007c33d44 100644 --- a/rotate_logs.h +++ b/rotate_logs.h @@ -17,24 +17,18 @@ #ifndef _ROTATE_LOGS_H #define _ROTATE_LOGS_H -#include +#include +#include -#include /* private pmsg functions */ +#include -constexpr int KEEP_LOG_COUNT = 10; +static constexpr int KEEP_LOG_COUNT = 10; -ssize_t logbasename(log_id_t /* logId */, - char /* prio */, - const char *filename, - const char * /* buf */, size_t len, - void *arg); +ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len, + void* arg); -ssize_t logrotate( - log_id_t logId, - char prio, - const char *filename, - const char *buf, size_t len, - void *arg); +ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, size_t len, + void* arg); // Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. // Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. -- cgit v1.2.3 From 647e39644542ae719fa5bab94dbe269528db06d3 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Wed, 19 Jul 2017 18:51:03 +0900 Subject: Build libminui with BOARD_VNDK_VERSION Use libdrm_platform which is a platform variant of libdrm. Bug: 63741047 Bug: 37342627 Test: BOARD_VNDK_VERSION=current m -j libminui Test: ryu recovery graphics test shows various graphics Change-Id: Ifd2c1432781a96538585cdf818aa728d628a2f5a Merged-In: Ifd2c1432781a96538585cdf818aa728d628a2f5a --- minui/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minui/Android.mk b/minui/Android.mk index ae1552b1b..546ba2fe5 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -28,7 +28,7 @@ LOCAL_SRC_FILES := \ LOCAL_WHOLE_STATIC_LIBRARIES := \ libadf \ - libdrm \ + libdrm_platform \ libsync_recovery LOCAL_STATIC_LIBRARIES := \ -- cgit v1.2.3 From 2ee9266827ac72bc046995f3d5318643ebc09752 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Thu, 19 Oct 2017 22:29:50 -0700 Subject: libdrm_platform is now libdrm Since it's converted to Soong and is using vendor_available. Test: mmma bootable/recovery Change-Id: I57b40c26abc2f6031241de893b3184ed1283e1fe --- minui/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minui/Android.mk b/minui/Android.mk index 546ba2fe5..ae1552b1b 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -28,7 +28,7 @@ LOCAL_SRC_FILES := \ LOCAL_WHOLE_STATIC_LIBRARIES := \ libadf \ - libdrm_platform \ + libdrm \ libsync_recovery LOCAL_STATIC_LIBRARIES := \ -- cgit v1.2.3 From 9bc56553dd63b3f8fa6662f03c75a825c79ff6f4 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 20 Oct 2017 01:50:04 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import Bug: 68003463 Change-Id: Iae8fc3afc1453b6b609dae2bbff08231d00735e7 --- tools/recovery_l10n/res/values-hi/strings.xml | 2 +- tools/recovery_l10n/res/values-mr/strings.xml | 8 ++++---- tools/recovery_l10n/res/values-pa/strings.xml | 6 +++--- tools/recovery_l10n/res/values-te/strings.xml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/recovery_l10n/res/values-hi/strings.xml b/tools/recovery_l10n/res/values-hi/strings.xml index a8a876ee4..65d003352 100644 --- a/tools/recovery_l10n/res/values-hi/strings.xml +++ b/tools/recovery_l10n/res/values-hi/strings.xml @@ -3,7 +3,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "सिस्टम अपडेट इंस्टॉल किया जा रहा है" "मिटाया जा रहा है" - "कोई आदेश नहीं" + "कोई निर्देश नहीं मिला" "गड़बड़ी!" "सुरक्षा अपडेट इंस्टॉल किया जा रहा है" diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml index 8cf86f773..5f820336f 100644 --- a/tools/recovery_l10n/res/values-mr/strings.xml +++ b/tools/recovery_l10n/res/values-mr/strings.xml @@ -1,9 +1,9 @@ - "सिस्टम अद्यतन स्थापित करीत आहे" + "सिस्टम अपडेट इंस्टॉल करत आहे" "मिटवत आहे" - "कोणताही आदेश नाही" - "त्रुटी!" - "सुरक्षा अद्यतन स्थापित करीत आहे" + "कोणतीही कमांड नाही" + "एरर!" + "सुरक्षा अपडेट इंस्टॉल करत आहे" diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml index 8564c9c36..27972d117 100644 --- a/tools/recovery_l10n/res/values-pa/strings.xml +++ b/tools/recovery_l10n/res/values-pa/strings.xml @@ -1,9 +1,9 @@ - "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" "ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ" - "ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ" + "ਕੋਈ ਆਦੇਸ਼ ਨਹੀਂ" "ਅਸ਼ੁੱਧੀ!" - "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml index cfb02c915..e35c82bc4 100644 --- a/tools/recovery_l10n/res/values-te/strings.xml +++ b/tools/recovery_l10n/res/values-te/strings.xml @@ -4,6 +4,6 @@ "సిస్టమ్ నవీకరణను ఇన్‍స్టాల్ చేస్తోంది" "డేటాను తొలగిస్తోంది" "ఆదేశం లేదు" - "లోపం సంభవించింది!" + "ఎర్రర్ సంభవించింది!" "భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది" -- cgit v1.2.3 From 5450c84ba4cc62b887e69ec11a922194b36756a9 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 18 Oct 2017 13:15:21 -0700 Subject: Finish the new data receiver when update fails The thread to receive new data may still be alive after we exit PerformBlockImageUpdate() upon failures. This caused memory corruption when we run the unittest repeatedly. Set the receiver_available flag to false and make sure the receiver exits when the update fails. Bug: 65430057 Test: unittests passed with tsan Change-Id: Icb232d13fb96c78262249ffbd29cdbe5b77f1fce --- tests/component/updater_test.cpp | 220 +++++++++++++++++++++++---------------- updater/blockimg.cpp | 57 +++++++--- 2 files changed, 174 insertions(+), 103 deletions(-) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index e6aec4ad6..266657d31 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,23 @@ static void expect(const char* expected, const char* expr_str, CauseCode cause_c ASSERT_EQ(cause_code, state.cause_code); } +static void BuildUpdatePackage(const std::unordered_map& entries, + int fd) { + FILE* zip_file_ptr = fdopen(fd, "wb"); + ZipWriter zip_writer(zip_file_ptr); + + for (const auto& entry : entries) { + ASSERT_EQ(0, zip_writer.StartEntry(entry.first.c_str(), 0)); + if (!entry.second.empty()) { + ASSERT_EQ(0, zip_writer.WriteBytes(entry.second.data(), entry.second.size())); + } + ASSERT_EQ(0, zip_writer.FinishEntry()); + } + + ASSERT_EQ(0, zip_writer.Finish()); + ASSERT_EQ(0, fclose(zip_file_ptr)); +} + static std::string get_sha1(const std::string& content) { uint8_t digest[SHA_DIGEST_LENGTH]; SHA1(reinterpret_cast(content.c_str()), content.size(), digest); @@ -420,30 +438,19 @@ TEST_F(UpdaterTest, show_progress) { ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); } -TEST_F(UpdaterTest, block_image_update) { - // Create a zip file with new_data and patch_data. - TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); - ZipWriter zip_writer(zip_file_ptr); - - // Add a dummy new data. - ASSERT_EQ(0, zip_writer.StartEntry("new_data", 0)); - ASSERT_EQ(0, zip_writer.FinishEntry()); - - // Generate and add the patch data. +TEST_F(UpdaterTest, block_image_update_patch_data) { std::string src_content = std::string(4096, 'a') + std::string(4096, 'c'); std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd'); + + // Generate the patch data. TemporaryFile patch_file; ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast(src_content.data()), src_content.size(), reinterpret_cast(tgt_content.data()), tgt_content.size(), patch_file.path, nullptr)); std::string patch_content; ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content)); - ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); - ASSERT_EQ(0, zip_writer.WriteBytes(patch_content.data(), patch_content.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add two transfer lists. The first one contains a bsdiff; and we expect the update to succeed. + // Create the transfer list that contains a bsdiff. std::string src_hash = get_sha1(src_content); std::string tgt_hash = get_sha1(tgt_content); std::vector transfer_list = { @@ -456,27 +463,16 @@ TEST_F(UpdaterTest, block_image_update) { src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()), "free " + src_hash, }; - ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); - std::string commands = android::base::Join(transfer_list, '\n'); - ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Stash and free some blocks, then fail the 2nd update intentionally. - std::vector fail_transfer_list = { - "4", - "2", - "0", - "2", - "stash " + tgt_hash + " 2,0,2", - "free " + tgt_hash, - "fail", + std::unordered_map entries = { + { "new_data", "" }, + { "patch_data", patch_content }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, }; - ASSERT_EQ(0, zip_writer.StartEntry("fail_transfer_list", 0)); - std::string fail_commands = android::base::Join(fail_transfer_list, '\n'); - ASSERT_EQ(0, zip_writer.WriteBytes(fail_commands.data(), fail_commands.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - ASSERT_EQ(0, zip_writer.Finish()); - ASSERT_EQ(0, fclose(zip_file_ptr)); + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); MemMapping map; ASSERT_TRUE(map.MapFile(zip_file.path)); @@ -491,7 +487,7 @@ TEST_F(UpdaterTest, block_image_update) { updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; - // Execute the commands in the 1st transfer list. + // Execute the commands in the transfer list. TemporaryFile update_file; ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); std::string script = "block_image_update(\"" + std::string(update_file.path) + @@ -502,44 +498,98 @@ TEST_F(UpdaterTest, block_image_update) { ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content)); ASSERT_EQ(tgt_hash, get_sha1(updated_content)); - // Expect the 2nd update to fail, but expect the stashed blocks to be freed. - script = "block_image_update(\"" + std::string(update_file.path) + - R"(", package_extract_file("fail_transfer_list"), "new_data", "patch_data"))"; + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, block_image_update_fail) { + std::string src_content(4096 * 2, 'e'); + std::string src_hash = get_sha1(src_content); + // Stash and free some blocks, then fail the update intentionally. + std::vector transfer_list = { + "4", "2", "0", "2", "stash " + src_hash + " 2,0,2", "free " + src_hash, "fail", + }; + + // Add a new data of 10 bytes to test the deadlock. + std::unordered_map entries = { + { "new_data", std::string(10, 0) }, + { "patch_data", "" }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, + }; + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + // Expect the stashed blocks to be freed. + std::string script = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "new_data", "patch_data"))"; expect("", script.c_str(), kNoCause, &updater_info); // Updater generates the stash name based on the input file name. std::string name_digest = get_sha1(update_file.path); std::string stash_base = "/cache/recovery/" + name_digest; ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); - ASSERT_EQ(-1, access((stash_base + tgt_hash).c_str(), F_OK)); + ASSERT_EQ(-1, access((stash_base + src_hash).c_str(), F_OK)); ASSERT_EQ(0, rmdir(stash_base.c_str())); ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } -TEST_F(UpdaterTest, new_data_short_write) { - // Create a zip file with new_data. +TEST_F(UpdaterTest, new_data_over_write) { + std::vector transfer_list = { + "4", "1", "0", "0", "new 2,0,1", + }; + + // Write 4096 + 100 bytes of new data. + std::unordered_map entries = { + { "new_data", std::string(4196, 0) }, + { "patch_data", "" }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, + }; + + // Build the update package. TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); - ZipWriter zip_writer(zip_file_ptr); + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); - // Add the empty new data. - ASSERT_EQ(0, zip_writer.StartEntry("empty_new_data", 0)); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add the short written new data. - ASSERT_EQ(0, zip_writer.StartEntry("short_new_data", 0)); - std::string new_data_short = std::string(10, 'a'); - ASSERT_EQ(0, zip_writer.WriteBytes(new_data_short.data(), new_data_short.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add the data of exactly one block. - ASSERT_EQ(0, zip_writer.StartEntry("exact_new_data", 0)); - std::string new_data_exact = std::string(4096, 'a'); - ASSERT_EQ(0, zip_writer.WriteBytes(new_data_exact.data(), new_data_exact.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add a dummy patch data. - ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); - ASSERT_EQ(0, zip_writer.FinishEntry()); + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + TemporaryFile update_file; + std::string script = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "new_data", "patch_data"))"; + expect("t", script.c_str(), kNoCause, &updater_info); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, new_data_short_write) { std::vector transfer_list = { "4", "1", @@ -547,12 +597,17 @@ TEST_F(UpdaterTest, new_data_short_write) { "0", "new 2,0,1", }; - ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); - std::string commands = android::base::Join(transfer_list, '\n'); - ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - ASSERT_EQ(0, zip_writer.Finish()); - ASSERT_EQ(0, fclose(zip_file_ptr)); + + std::unordered_map entries = { + { "empty_new_data", "" }, + { "short_new_data", std::string(10, 'a') }, + { "exact_new_data", std::string(4096, 'a') }, + { "patch_data", "" }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, + }; + + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); MemMapping map; ASSERT_TRUE(map.MapFile(zip_file.path)); @@ -587,14 +642,6 @@ TEST_F(UpdaterTest, new_data_short_write) { } TEST_F(UpdaterTest, brotli_new_data) { - // Create a zip file with new_data. - TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); - ZipWriter zip_writer(zip_file_ptr); - - // Add a brotli compressed new data entry. - ASSERT_EQ(0, zip_writer.StartEntry("new.dat.br", 0)); - auto generator = []() { return rand() % 128; }; // Generate 100 blocks of random data. std::string brotli_new_data; @@ -602,16 +649,12 @@ TEST_F(UpdaterTest, brotli_new_data) { generate_n(back_inserter(brotli_new_data), 4096 * 100, generator); size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size()); - std::vector encoded_data(encoded_size); + std::string encoded_data(encoded_size, 0); ASSERT_TRUE(BrotliEncoderCompress( BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, brotli_new_data.size(), - reinterpret_cast(brotli_new_data.data()), &encoded_size, encoded_data.data())); - - ASSERT_EQ(0, zip_writer.WriteBytes(encoded_data.data(), encoded_size)); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add a dummy patch data. - ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); - ASSERT_EQ(0, zip_writer.FinishEntry()); + reinterpret_cast(brotli_new_data.data()), &encoded_size, + reinterpret_cast(const_cast(encoded_data.data())))); + encoded_data.resize(encoded_size); // Write a few small chunks of new data, then a large chunk, and finally a few small chunks. // This helps us to catch potential short writes. @@ -627,12 +670,15 @@ TEST_F(UpdaterTest, brotli_new_data) { "new 2,98,99", "new 2,99,100", }; - ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); - std::string commands = android::base::Join(transfer_list, '\n'); - ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - ASSERT_EQ(0, zip_writer.Finish()); - ASSERT_EQ(0, fclose(zip_file_ptr)); + + std::unordered_map entries = { + { "new.dat.br", std::move(encoded_data) }, + { "patch_data", "" }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, + }; + + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); MemMapping map; ASSERT_TRUE(map.MapFile(zip_file.path)); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index ce3cea4f3..6c7b3efcf 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -281,6 +281,11 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { // Wait for nti->writer to be non-null, indicating some of this data is wanted. pthread_mutex_lock(&nti->mu); while (nti->writer == nullptr) { + // End the new data receiver if we encounter an error when performing block image update. + if (!nti->receiver_available) { + pthread_mutex_unlock(&nti->mu); + return false; + } pthread_cond_wait(&nti->cv, &nti->mu); } pthread_mutex_unlock(&nti->mu); @@ -316,6 +321,11 @@ static bool receive_brotli_new_data(const uint8_t* data, size_t size, void* cook // Wait for nti->writer to be non-null, indicating some of this data is wanted. pthread_mutex_lock(&nti->mu); while (nti->writer == nullptr) { + // End the receiver if we encounter an error when performing block image update. + if (!nti->receiver_available) { + pthread_mutex_unlock(&nti->mu); + return false; + } pthread_cond_wait(&nti->cv, &nti->mu); } pthread_mutex_unlock(&nti->mu); @@ -1591,29 +1601,44 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, } } + rc = 0; + +pbiudone: if (params.canwrite) { - pthread_join(params.thread, nullptr); + pthread_mutex_lock(¶ms.nti.mu); + if (params.nti.receiver_available) { + LOG(WARNING) << "new data receiver is still available after executing all commands."; + } + params.nti.receiver_available = false; + pthread_cond_broadcast(¶ms.nti.cv); + pthread_mutex_unlock(¶ms.nti.mu); + int ret = pthread_join(params.thread, nullptr); + if (ret != 0) { + LOG(WARNING) << "pthread join returned with " << strerror(ret); + } - LOG(INFO) << "wrote " << params.written << " blocks; expected " << total_blocks; - LOG(INFO) << "stashed " << params.stashed << " blocks"; - LOG(INFO) << "max alloc needed was " << params.buffer.size(); + if (rc == 0) { + LOG(INFO) << "wrote " << params.written << " blocks; expected " << total_blocks; + LOG(INFO) << "stashed " << params.stashed << " blocks"; + LOG(INFO) << "max alloc needed was " << params.buffer.size(); - const char* partition = strrchr(blockdev_filename->data.c_str(), '/'); - if (partition != nullptr && *(partition + 1) != 0) { - fprintf(cmd_pipe, "log bytes_written_%s: %zu\n", partition + 1, params.written * BLOCKSIZE); - fprintf(cmd_pipe, "log bytes_stashed_%s: %zu\n", partition + 1, params.stashed * BLOCKSIZE); - fflush(cmd_pipe); + const char* partition = strrchr(blockdev_filename->data.c_str(), '/'); + if (partition != nullptr && *(partition + 1) != 0) { + fprintf(cmd_pipe, "log bytes_written_%s: %zu\n", partition + 1, params.written * BLOCKSIZE); + fprintf(cmd_pipe, "log bytes_stashed_%s: %zu\n", partition + 1, params.stashed * BLOCKSIZE); + fflush(cmd_pipe); + } + // Delete stash only after successfully completing the update, as it may contain blocks needed + // to complete the update later. + DeleteStash(params.stashbase); } - // Delete stash only after successfully completing the update, as it may contain blocks needed - // to complete the update later. - DeleteStash(params.stashbase); - } else { + + pthread_mutex_destroy(¶ms.nti.mu); + pthread_cond_destroy(¶ms.nti.cv); + } else if (rc == 0) { LOG(INFO) << "verified partition contents; update may be resumed"; } - rc = 0; - -pbiudone: if (ota_fsync(params.fd) == -1) { failure_type = kFsyncFailure; PLOG(ERROR) << "fsync failed"; -- cgit v1.2.3 From fa188268e43ab75732a480d6b2ec748d9d0dbfae Mon Sep 17 00:00:00 2001 From: Alex Deymo Date: Tue, 10 Oct 2017 17:56:17 +0200 Subject: Use SuffixArrayIndexInterface opaque type instead of the underlying data pointer. bsdiff interface is changing such that it hides the suffix array pointer from the public interface. This allows to use a different suffix array data size depending on the input size, running much faster in the normal case. Bug: 34220646 Test: `make checkbuild`; Ran an incremental update generation on a non-A/B device. Change-Id: I78e766da56cf28bc7774b8c8e58527bc11d919fb --- applypatch/imgdiff.cpp | 12 +++++++----- applypatch/include/applypatch/imgdiff_image.h | 5 +++-- tests/component/updater_test.cpp | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index c887a854d..ccd68dc3e 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -161,7 +161,7 @@ #include #include #include -#include +#include #include #include @@ -322,7 +322,8 @@ void ImageChunk::MergeAdjacentNormal(const ImageChunk& other) { } bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src, - std::vector* patch_data, saidx_t** bsdiff_cache) { + std::vector* patch_data, + bsdiff::SuffixArrayIndexInterface** bsdiff_cache) { #if defined(__ANDROID__) char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX"; #else @@ -1081,7 +1082,7 @@ bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image, printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks()); patch_chunks->clear(); - saidx_t* bsdiff_cache = nullptr; + bsdiff::SuffixArrayIndexInterface* bsdiff_cache = nullptr; for (size_t i = 0; i < tgt_image.NumOfChunks(); i++) { const auto& tgt_chunk = tgt_image[i]; @@ -1095,7 +1096,8 @@ bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image, : src_image.FindChunkByName(tgt_chunk.GetEntryName()); const auto& src_ref = (src_chunk == nullptr) ? src_image.PseudoSource() : *src_chunk; - saidx_t** bsdiff_cache_ptr = (src_chunk == nullptr) ? &bsdiff_cache : nullptr; + bsdiff::SuffixArrayIndexInterface** bsdiff_cache_ptr = + (src_chunk == nullptr) ? &bsdiff_cache : nullptr; std::vector patch_data; if (!ImageChunk::MakePatch(tgt_chunk, src_ref, &patch_data, bsdiff_cache_ptr)) { @@ -1112,7 +1114,7 @@ bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image, patch_chunks->emplace_back(tgt_chunk, src_ref, std::move(patch_data)); } } - free(bsdiff_cache); + delete bsdiff_cache; CHECK_EQ(patch_chunks->size(), tgt_image.NumOfChunks()); return true; diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h index 491043dc1..4e915e5e7 100644 --- a/applypatch/include/applypatch/imgdiff_image.h +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -98,7 +98,8 @@ class ImageChunk { * repeatedly, pass nullptr if not needed. */ static bool MakePatch(const ImageChunk& tgt, const ImageChunk& src, - std::vector* patch_data, saidx_t** bsdiff_cache); + std::vector* patch_data, + bsdiff::SuffixArrayIndexInterface** bsdiff_cache); private: const uint8_t* GetRawData() const; diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 2a0575a31..19653ec50 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From fdec10333591a885bdc1667088811cdc8bceffb7 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 24 Oct 2017 12:25:41 -0700 Subject: applypatch: Fix a memory leak in ApplyImagePatch(). $ valgrind --leak-check=full out/host/linux-x86/nativetest64/recovery_host_test/recovery_host_test ==36755== 112 bytes in 1 blocks are definitely lost in loss record 4 of 16 ==36755== at 0x40307C4: malloc (valgrind/coregrind/m_replacemalloc/vg_replace_malloc.c:270) ==36755== by 0x40C1669: operator new(unsigned long) (external/libcxxabi/src/cxa_new_delete.cpp:46) ==36755== by 0x18D6A8: ApplyImagePatch(unsigned char const*, unsigned long, Value const*, std::__1::function, sha_state_st*, Value const*) (bootable/recovery/applypatch/imgpatch.cpp:62) ==36755== by 0x18D02B: ApplyImagePatch(unsigned char const*, unsigned long, unsigned char const*, unsigned long, std::__1::function) (bootable/recovery/applypatch/imgpatch.cpp:134) ==36755== by 0x160D15: GenerateTarget(std::__1::basic_string, std::__1::allocator > const&, std::__1::basic_string, std::__1::allocator > const&, std::__1::basic_string, std::__1::allocator >*) (bootable/recovery/tests/component/imgdiff_test.cpp:85) ==36755== by 0x11FA7D: verify_patched_image(std::__1::basic_string, std::__1::allocator > const&, std::__1::basic_string, std::__1::allocator > const&, std::__1::basic_string, std::__1::allocator > const&) (bootable/recovery/tests/component/imgdiff_test.cpp:96) ==36755== by 0x12966C: ImgdiffTest_zip_mode_smoke_trailer_zeros_Test::TestBody() (bootable/recovery/tests/component/imgdiff_test.cpp:295) ==36755== by 0x235EF9: testing::Test::Run() (external/googletest/googletest/src/gtest.cc:2455) ==36755== by 0x236CBF: testing::TestInfo::Run() (external/googletest/googletest/src/gtest.cc:2653) ==36755== by 0x2372D6: testing::TestCase::Run() (external/googletest/googletest/src/gtest.cc:2771) ==36755== by 0x23EEE6: testing::internal::UnitTestImpl::RunAllTests() (external/googletest/googletest/src/gtest.cc:4648) ==36755== by 0x23EB45: testing::UnitTest::Run() (external/googletest/googletest/src/gtest.cc:2455) std::unique_ptr strm(new z_stream(), deflateEnd); Only the internally allocated buffers inside 'strm' would be free'd by deflateEnd(), but not 'strm' itself. This CL fixes the issue by moving 'strm' to stack variable. Note that we only need to call deflateEnd() on successful return of deflateInit2(). Test: recovery_host_test && recovery_component_test Change-Id: I39b9bdf62376b8029f95cab82c8542bfcb874009 --- applypatch/imgpatch.cpp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 7a43ddbef..25ba0a182 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -59,13 +59,13 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_ int mem_level = Read4(deflate_header + 52); int strategy = Read4(deflate_header + 56); - std::unique_ptr strm(new z_stream(), deflateEnd); - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - strm->avail_in = 0; - strm->next_in = nullptr; - int ret = deflateInit2(strm.get(), level, method, window_bits, mem_level, strategy); + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = nullptr; + int ret = deflateInit2(&strm, level, method, window_bits, mem_level, strategy); if (ret != Z_OK) { LOG(ERROR) << "Failed to init uncompressed data deflation: " << ret; return false; @@ -76,18 +76,19 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_ size_t actual_target_length = 0; size_t total_written = 0; static constexpr size_t buffer_size = 32768; - auto compression_sink = [&](const uint8_t* data, size_t len) -> size_t { + auto compression_sink = [&strm, &actual_target_length, &expected_target_length, &total_written, + &ret, &ctx, &sink](const uint8_t* data, size_t len) -> size_t { // The input patch length for an update never exceeds INT_MAX. - strm->avail_in = len; - strm->next_in = data; + strm.avail_in = len; + strm.next_in = data; do { std::vector buffer(buffer_size); - strm->avail_out = buffer_size; - strm->next_out = buffer.data(); + strm.avail_out = buffer_size; + strm.next_out = buffer.data(); if (actual_target_length + len < expected_target_length) { - ret = deflate(strm.get(), Z_NO_FLUSH); + ret = deflate(&strm, Z_NO_FLUSH); } else { - ret = deflate(strm.get(), Z_FINISH); + ret = deflate(&strm, Z_FINISH); } if (ret != Z_OK && ret != Z_STREAM_END) { LOG(ERROR) << "Failed to deflate stream: " << ret; @@ -95,20 +96,24 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_ return 0; } - size_t have = buffer_size - strm->avail_out; + size_t have = buffer_size - strm.avail_out; total_written += have; if (sink(buffer.data(), have) != have) { LOG(ERROR) << "Failed to write " << have << " compressed bytes to output."; return 0; } if (ctx) SHA1_Update(ctx, buffer.data(), have); - } while ((strm->avail_in != 0 || strm->avail_out == 0) && ret != Z_STREAM_END); + } while ((strm.avail_in != 0 || strm.avail_out == 0) && ret != Z_STREAM_END); actual_target_length += len; return len; }; - if (ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink, nullptr) != 0) { + int bspatch_result = + ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink, nullptr); + deflateEnd(&strm); + + if (bspatch_result != 0) { return false; } -- cgit v1.2.3 From f29ed3e843409e77db2423e13ac4041cf1445229 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 24 Oct 2017 16:48:32 -0700 Subject: tests: Take the ownership of the FD when calling fdopen. To avoid closing the same FD twice. Test: recovery_component_test && recovery_host_test Change-Id: I95195be8109101081410e9224efda535b2560e72 --- tests/component/imgdiff_test.cpp | 32 ++++++++++++++++---------------- tests/component/install_test.cpp | 26 +++++++++++++------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index bf591dae4..f82a9db57 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -148,7 +148,7 @@ TEST(ImgdiffTest, image_mode_smoke) { TEST(ImgdiffTest, zip_mode_smoke_store) { // Construct src and tgt zip files. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode. const std::string src_content("abcdefg"); @@ -158,7 +158,7 @@ TEST(ImgdiffTest, zip_mode_smoke_store) { ASSERT_EQ(0, fclose(src_file_ptr)); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode. const std::string tgt_content("abcdefgxyz"); @@ -197,7 +197,7 @@ TEST(ImgdiffTest, zip_mode_smoke_store) { TEST(ImgdiffTest, zip_mode_smoke_compressed) { // Construct src and tgt zip files. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress)); const std::string src_content("abcdefg"); @@ -207,7 +207,7 @@ TEST(ImgdiffTest, zip_mode_smoke_compressed) { ASSERT_EQ(0, fclose(src_file_ptr)); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress)); const std::string tgt_content("abcdefgxyz"); @@ -246,7 +246,7 @@ TEST(ImgdiffTest, zip_mode_smoke_compressed) { TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { // Construct src and tgt zip files. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress)); const std::string src_content("abcdefg"); @@ -256,7 +256,7 @@ TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { ASSERT_EQ(0, fclose(src_file_ptr)); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress)); const std::string tgt_content("abcdefgxyz"); @@ -761,7 +761,7 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { // 3 blocks 'a' 12 blocks 'd' (exceeds limit) // 3 blocks 'e' TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); construct_store_entry( { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } }, @@ -770,7 +770,7 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { ASSERT_EQ(0, fclose(tgt_file_ptr)); TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } }, &src_writer); @@ -792,7 +792,7 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { std::string tgt; ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - // Expect 4 pieces of patch.(Rougly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e') + // Expect 4 pieces of patch. (Roughly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e') GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt); } @@ -810,7 +810,7 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { // 8 blocks, "c" 20 blocks, "d" (exceeds limit) // 1 block, "f" 2 blocks, "e" TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); construct_deflate_entry( @@ -821,7 +821,7 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { ASSERT_EQ(0, fclose(tgt_file_ptr)); TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); construct_deflate_entry( @@ -911,7 +911,7 @@ TEST(ImgdiffTest, zip_mode_no_match_source) { generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer, @@ -922,7 +922,7 @@ TEST(ImgdiffTest, zip_mode_no_match_source) { // We don't have a matching source entry. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); construct_store_entry({ { "d", 1, 'd' } }, &src_writer); ASSERT_EQ(0, src_writer.Finish()); @@ -954,7 +954,7 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data); @@ -964,7 +964,7 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { // Construct 10 blocks of source. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data); ASSERT_EQ(0, src_writer.Finish()); @@ -985,6 +985,6 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { std::string tgt; ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - // Expect 1 pieces of patch since limit is larger than the zip file size. + // Expect 1 piece of patch since limit is larger than the zip file size. GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); } diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 7bb496066..d19d788e4 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -37,7 +37,7 @@ TEST(InstallTest, verify_package_compatibility_no_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); // The archive must have something to be opened correctly. ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); @@ -54,7 +54,7 @@ TEST(InstallTest, verify_package_compatibility_no_entry) { TEST(InstallTest, verify_package_compatibility_invalid_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("compatibility.zip", 0)); ASSERT_EQ(0, writer.FinishEntry()); @@ -70,7 +70,7 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) { TEST(InstallTest, read_metadata_from_package_smoke) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); const std::string content("abcdefg"); @@ -87,7 +87,7 @@ TEST(InstallTest, read_metadata_from_package_smoke) { CloseArchive(zip); TemporaryFile temp_file2; - FILE* zip_file2 = fdopen(temp_file2.fd, "w"); + FILE* zip_file2 = fdopen(temp_file2.release(), "w"); ZipWriter writer2(zip_file2); ASSERT_EQ(0, writer2.StartEntry("META-INF/com/android/metadata", kCompressDeflated)); ASSERT_EQ(0, writer2.WriteBytes(content.data(), content.size())); @@ -104,7 +104,7 @@ TEST(InstallTest, read_metadata_from_package_smoke) { TEST(InstallTest, read_metadata_from_package_no_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("dummy_entry", kCompressStored)); ASSERT_EQ(0, writer.FinishEntry()); @@ -120,7 +120,7 @@ TEST(InstallTest, read_metadata_from_package_no_entry) { TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { TemporaryFile compatibility_zip_file; - FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w"); + FILE* compatibility_zip = fdopen(compatibility_zip_file.release(), "w"); ZipWriter compatibility_zip_writer(compatibility_zip); ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); std::string malformed_xml = "malformed"; @@ -130,7 +130,7 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { ASSERT_EQ(0, fclose(compatibility_zip)); TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); std::string compatibility_zip_content; @@ -165,7 +165,7 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml ASSERT_TRUE( android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content)); TemporaryFile compatibility_zip_file; - FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w"); + FILE* compatibility_zip = fdopen(compatibility_zip_file.release(), "w"); ZipWriter compatibility_zip_writer(compatibility_zip); ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(system_manifest_xml_content.data(), @@ -175,7 +175,7 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml ASSERT_EQ(0, fclose(compatibility_zip)); TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); std::string compatibility_zip_content; @@ -202,7 +202,7 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml #ifdef AB_OTA_UPDATER static void VerifyAbUpdateBinaryCommand(const std::string& serialno, bool success = true) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); ASSERT_EQ(0, writer.FinishEntry()); @@ -258,7 +258,7 @@ TEST(InstallTest, update_binary_command_smoke) { VerifyAbUpdateBinaryCommand({}); #else TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; ASSERT_EQ(0, writer.StartEntry(UPDATE_BINARY_NAME, kCompressStored)); @@ -303,7 +303,7 @@ TEST(InstallTest, update_binary_command_smoke) { TEST(InstallTest, update_binary_command_invalid) { #ifdef AB_OTA_UPDATER TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); // Missing payload_properties.txt. ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); @@ -334,7 +334,7 @@ TEST(InstallTest, update_binary_command_invalid) { CloseArchive(zip); #else TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); // The archive must have something to be opened correctly. ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); -- cgit v1.2.3 From 6ec94c023e2752c5c5f16f03c7832bdeaa4a2d91 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 27 Oct 2017 23:39:45 -0700 Subject: update_verifier: Fix the wrong computation with group_range_count. 'group_range_count' doesn't properly consider the pair-wise range structure. It may split the ranges into wrong pairs if it evaluates to an odd number. For example, for an input range string of "6,0,2,10,12,20,22" with 4 threads, group_range_count becomes 1. It would then try to verify (0,2), (2,10), (10,12) and (12,20). Note that (2,10) and (12,20) are not valid ranges to be verified, and with (20,22) uncovered. Bug: 68343761 Test: Trigger update_verifier verification. Check the number of verified blocks against the one in care_map.txt. Change-Id: I7c5769325d9866be06c45e7dbcc0c8ea266de714 --- update_verifier/update_verifier.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index faebbede0..ba7b7aec4 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -137,11 +137,12 @@ static bool read_blocks(const std::string& partition, const std::string& range_s LOG(ERROR) << "Error in parsing range string."; return false; } + range_count /= 2; std::vector> threads; size_t thread_num = std::thread::hardware_concurrency() ?: 4; - thread_num = std::min(thread_num, range_count / 2); - size_t group_range_count = range_count / thread_num; + thread_num = std::min(thread_num, range_count); + size_t group_range_count = (range_count + thread_num - 1) / thread_num; for (size_t t = 0; t < thread_num; t++) { auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() { @@ -154,7 +155,8 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } - for (size_t i = 1 + group_range_count * t; i < group_range_count * (t + 1) + 1; i += 2) { + for (size_t i = group_range_count * 2 * t + 1; + i < std::min(group_range_count * 2 * (t + 1) + 1, ranges.size()); i += 2) { unsigned int range_start, range_end; bool parse_status = android::base::ParseUint(ranges[i], &range_start); parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); -- cgit v1.2.3 From 559a6d1d2ae2e5145641e1eb16e2c015d756d8c9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 27 Oct 2017 23:39:45 -0700 Subject: update_verifier: Fix the wrong computation with group_range_count. 'group_range_count' doesn't properly consider the pair-wise range structure. It may split the ranges into wrong pairs if it evaluates to an odd number. For example, for an input range string of "6,0,2,10,12,20,22" with 4 threads, group_range_count becomes 1. It would then try to verify (0,2), (2,10), (10,12) and (12,20). Note that (2,10) and (12,20) are not valid ranges to be verified, and with (20,22) uncovered. Bug: 68343761 Test: Trigger update_verifier verification. Check the number of verified blocks against the one in care_map.txt. Change-Id: I7c5769325d9866be06c45e7dbcc0c8ea266de714 (cherry picked from commit 62caeb5f48c9d7b1a8ed97c4a021195b8499b804) --- update_verifier/update_verifier.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index faebbede0..ba7b7aec4 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -137,11 +137,12 @@ static bool read_blocks(const std::string& partition, const std::string& range_s LOG(ERROR) << "Error in parsing range string."; return false; } + range_count /= 2; std::vector> threads; size_t thread_num = std::thread::hardware_concurrency() ?: 4; - thread_num = std::min(thread_num, range_count / 2); - size_t group_range_count = range_count / thread_num; + thread_num = std::min(thread_num, range_count); + size_t group_range_count = (range_count + thread_num - 1) / thread_num; for (size_t t = 0; t < thread_num; t++) { auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() { @@ -154,7 +155,8 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } - for (size_t i = 1 + group_range_count * t; i < group_range_count * (t + 1) + 1; i += 2) { + for (size_t i = group_range_count * 2 * t + 1; + i < std::min(group_range_count * 2 * (t + 1) + 1, ranges.size()); i += 2) { unsigned int range_start, range_end; bool parse_status = android::base::ParseUint(ranges[i], &range_start); parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); -- cgit v1.2.3 From 113fe05ee0d33dbafd0923be97d448feb27d8f4b Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 24 Oct 2017 16:12:35 -0700 Subject: Fix the size mismatch in imgdiff As we construct the deflate entries of the target zip file with random data, the total size of the zip file may vary from case to case. This leads to occasional failures in the split test for deflate large apk files. This CL fixes the issue by adding two static zip files in the testdata instead of generating them dynamically. Bug: 67849209 Test: run the deflate_large_test repeatedly Change-Id: Iaeffad9205adefa10c9f62f9f088c33c4360a650 --- tests/common/test_constants.h | 12 ++++-- tests/component/imgdiff_test.cpp | 77 ++++++++++++++------------------------- tests/testdata/deflate_src.zip | Bin 0 -> 164491 bytes tests/testdata/deflate_tgt.zip | Bin 0 -> 160385 bytes 4 files changed, 36 insertions(+), 53 deletions(-) create mode 100644 tests/testdata/deflate_src.zip create mode 100644 tests/testdata/deflate_tgt.zip diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h index f6b6922a4..514818e0a 100644 --- a/tests/common/test_constants.h +++ b/tests/common/test_constants.h @@ -19,6 +19,8 @@ #include +#include + // Zip entries in ziptest_valid.zip. static const std::string kATxtContents("abcdefghabcdefgh\n"); static const std::string kBTxtContents("abcdefgh\n"); @@ -30,10 +32,14 @@ static const std::string kATxtSha1Sum("32c96a03dc8cd20097940f351bca6261ee5a1643" // echo -n -e "abcdefgh\n" | sha1sum static const std::string kBTxtSha1Sum("e414af7161c9554089f4106d6f1797ef14a73666"); -static const char* data_root = getenv("ANDROID_DATA"); - static std::string from_testdata_base(const std::string& fname) { - return std::string(data_root) + "/nativetest/recovery/testdata/" + fname; +#ifdef __ANDROID__ + static std::string data_root = getenv("ANDROID_DATA"); +#else + static std::string data_root = std::string(getenv("ANDROID_PRODUCT_OUT")) + "/data"; +#endif + + return data_root + "/nativetest/recovery/testdata/" + fname; } #endif // _OTA_TEST_CONSTANTS_H diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index f82a9db57..6de804e06 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -32,6 +32,8 @@ #include #include +#include "common/test_constants.h" + using android::base::get_unaligned; // Sanity check for the given imgdiff patch header. @@ -797,44 +799,21 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { } TEST(ImgdiffTest, zip_mode_deflate_large_apk) { - // Generate 50 blocks of random data. - std::string random_data; - random_data.reserve(4096 * 50); - generate_n(back_inserter(random_data), 4096 * 50, []() { return rand() % 256; }); - - // Construct src and tgt zip files with limit = 10 blocks. + // Src and tgt zip files are constructed as follows. // src tgt // 22 blocks, "d" 4 blocks, "a" // 5 blocks, "b" 4 blocks, "b" - // 3 blocks, "a" 7 blocks, "c" (exceeds limit) - // 8 blocks, "c" 20 blocks, "d" (exceeds limit) - // 1 block, "f" 2 blocks, "e" - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - - construct_deflate_entry( - { { "a", 0, 4 }, { "b", 5, 4 }, { "c", 12, 8 }, { "d", 21, 20 }, { "e", 45, 2 }, - { "f", 48, 1 } }, &tgt_writer, random_data); - - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - - construct_deflate_entry( - { { "d", 21, 22 }, { "b", 5, 5 }, { "a", 0, 3 }, { "g", 9, 1 }, { "c", 11, 8 }, - { "f", 45, 1 } }, &src_writer, random_data); - - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); + // 3 blocks, "a" 8 blocks, "c" (exceeds limit) + // 1 block, "g" 20 blocks, "d" (exceeds limit) + // 8 blocks, "c" 2 blocks, "e" + // 1 block, "f" 1 block , "f" + std::string tgt_path = from_testdata_base("deflate_tgt.zip"); + std::string src_path = from_testdata_base("deflate_src.zip"); ZipModeImage src_image(true, 10 * 4096); ZipModeImage tgt_image(false, 10 * 4096); - ASSERT_TRUE(src_image.Initialize(src_file.path)); - ASSERT_TRUE(tgt_image.Initialize(tgt_file.path)); + ASSERT_TRUE(src_image.Initialize(src_path)); + ASSERT_TRUE(tgt_image.Initialize(tgt_path)); ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)); src_image.DumpChunks(); @@ -846,11 +825,12 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, &split_src_images, &split_src_ranges); - // src_piece 1: a 3 blocks, b 5 blocks - // src_piece 2: c 8 blocks - // src_piece 3: d-0 10 block - // src_piece 4: d-1 10 blocks - // src_piece 5: e 1 block, CD + // Expected split images with limit = 10 blocks. + // src_piece 0: a 3 blocks, b 5 blocks + // src_piece 1: c 8 blocks + // src_piece 2: d-0 10 block + // src_piece 3: d-1 10 blocks + // src_piece 4: e 1 block, CD ASSERT_EQ(split_tgt_images.size(), split_src_images.size()); ASSERT_EQ(static_cast(5), split_tgt_images.size()); @@ -883,24 +863,21 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { ASSERT_EQ("2", android::base::Trim(info_list[0])); ASSERT_EQ("5", android::base::Trim(info_list[1])); - std::vector patch_size; + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_path, &tgt)); + ASSERT_EQ(static_cast(160385), tgt.size()); + std::vector tgt_file_ranges = { + "36864 2,22,31", "32768 2,31,40", "40960 2,0,11", "40960 2,11,21", "8833 4,21,22,40,41", + }; + for (size_t i = 0; i < 5; i++) { - struct stat st = {}; + struct stat st; std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i); ASSERT_EQ(0, stat(path.c_str(), &st)); - patch_size.push_back(st.st_size); + ASSERT_EQ(std::to_string(st.st_size) + " " + tgt_file_ranges[i], + android::base::Trim(info_list[i + 2])); } - ASSERT_EQ(std::to_string(patch_size[0]) + " 36864 2,22,31", android::base::Trim(info_list[2])); - ASSERT_EQ(std::to_string(patch_size[1]) + " 32768 2,31,40", android::base::Trim(info_list[3])); - ASSERT_EQ(std::to_string(patch_size[2]) + " 40960 2,0,11", android::base::Trim(info_list[4])); - ASSERT_EQ(std::to_string(patch_size[3]) + " 40960 2,11,21", android::base::Trim(info_list[5])); - ASSERT_EQ(std::to_string(patch_size[4]) + " 8833 4,21,22,40,41", - android::base::Trim(info_list[6])); - - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt); } diff --git a/tests/testdata/deflate_src.zip b/tests/testdata/deflate_src.zip new file mode 100644 index 000000000..bdb2b3216 Binary files /dev/null and b/tests/testdata/deflate_src.zip differ diff --git a/tests/testdata/deflate_tgt.zip b/tests/testdata/deflate_tgt.zip new file mode 100644 index 000000000..2a21760ec Binary files /dev/null and b/tests/testdata/deflate_tgt.zip differ -- cgit v1.2.3 From 8c28081039fdbdcf4e49f5d35f00b7c862c73b3d Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Sat, 28 Oct 2017 22:13:16 -0700 Subject: Add libbrotli as a dependency for libbsdiff Bug: 34220646 Test: mma Change-Id: If00285943fff8226f1bc7239db5570a277739904 --- applypatch/Android.bp | 1 + tests/Android.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/applypatch/Android.bp b/applypatch/Android.bp index 922f67abf..4d2d4b76a 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -158,6 +158,7 @@ cc_binary_host { "libbase", "libutils", "liblog", + "libbrotli", "libbz", "libz", ], diff --git a/tests/Android.mk b/tests/Android.mk index b0f71a832..8ebb60308 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -208,6 +208,7 @@ LOCAL_STATIC_LIBRARIES := \ libutils \ libbase \ libcrypto \ + libbrotli \ libbz \ libdivsufsort64 \ libdivsufsort \ -- cgit v1.2.3 From 54c1db4b3e6becf85f2b685f16425244b24138a6 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 1 Nov 2017 22:21:55 -0700 Subject: tests: Clean up the files in TemporaryDir. ~TemporaryDir() calls rmdir(2) directly, which works with empty directories only. Test: Run recovery_host_test; No leftover on host. Test; Run recovery_component_test on marlin; No leftover on device. Change-Id: Ib510efb16eeda61b34161e2b386499e6cb79a4ca --- tests/component/imgdiff_test.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 6de804e06..728b6cc76 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -657,19 +657,23 @@ static void construct_deflate_entry(const std::vector Date: Thu, 2 Nov 2017 11:58:12 -0700 Subject: recovery: remove make_ext4fs from updater Bug: 64395169 Change-Id: I6f6a4f82b225435c6ad5c828e110fa135e6f7579 --- updater/install.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/updater/install.cpp b/updater/install.cpp index 9425d1872..b865081b4 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -284,14 +283,8 @@ Value* FormatFn(const char* name, State* state, const std::vector(mke2fs_argv)); if (status != 0) { - LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location - << ", falling back to make_ext4fs"; - status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle); - if (status != 0) { - LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location; - return StringValue(""); - } - return StringValue(location); + LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location; + return StringValue(""); } const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-a", mount_point.c_str(), -- cgit v1.2.3 From 6b1aae376a06837d03e96fd19e842d995d967c1f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 26 Sep 2017 15:37:59 -0700 Subject: Add a repohook to clang-format changes. It would clang-format according to the local style file in .clang-format, unless explicitly skipped with --no-verify. An example output is as follows: [COMMIT dda6b1ee4247] test [FAILED] clang_format The following files have formatting errors: screen_ui.cpp You can run `/mnt/aosp/aosp-master/tools/repohooks/tools/clang-format.py --fix --clang-format /mnt/aosp/aosp-master/prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format --git-clang-format /mnt/aosp/aosp-master/prebuilts/clang/host/linux-x86/clang-stable/bin/git-clang-format --style file --commit dda6b1ee424710760bbab4421e95239fa6a2b40d` to fix this [COMMIT be69a2c4ba16] Add a repohook to clang-format the change. [RUNNING 2/2] clang_format An automatic fix can be attempted for the "clang_format" hook. Do you want to run it? (Yes/no)? Fix successfully applied. Amend the current commit before attempting to upload again. More details about repohooks can be found at: https://android.googlesource.com/platform/tools/repohooks/ Test: `repo upload` a CL. Change-Id: Ie8203a317eb3be7acd5592e03374873997647aa0 --- PREUPLOAD.cfg | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 PREUPLOAD.cfg diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg new file mode 100644 index 000000000..b5f5f0362 --- /dev/null +++ b/PREUPLOAD.cfg @@ -0,0 +1,6 @@ +[Builtin Hooks] +clang_format = true + +[Builtin Hooks Options] +# Handle native codes only. +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp -- cgit v1.2.3 From 000148dbc9921ab94c2b6bfc1843710a28c0a9ab Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 3 Nov 2017 11:52:33 -0700 Subject: recovery: fix library dependency After removing some deadcode from libext4_utils, libz is optimized out by linker. However, it's still required by libvintf. Moving libz down the list fixed the build. Bug: 64395169 Change-Id: I23ecd70c83af83a219faced59d8227dc3c4e43d5 --- Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index 7e34c10f9..985131b02 100644 --- a/Android.mk +++ b/Android.mk @@ -158,7 +158,6 @@ LOCAL_STATIC_LIBRARIES := \ libziparchive \ libotautil \ libmounts \ - libz \ libminadbd \ libasyncio \ libfusesideload \ @@ -173,7 +172,8 @@ LOCAL_STATIC_LIBRARIES := \ libcutils \ libutils \ liblog \ - libselinux + libselinux \ + libz LOCAL_HAL_STATIC_LIBRARIES := libhealthd -- cgit v1.2.3 From 502fd1c5e82a21ecb4eace746c320dc42a77144b Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 2 Nov 2017 11:58:12 -0700 Subject: recovery: remove make_ext4fs from updater Bug: 64395169 Change-Id: I6f6a4f82b225435c6ad5c828e110fa135e6f7579 --- updater/install.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/updater/install.cpp b/updater/install.cpp index 9425d1872..b865081b4 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -284,14 +283,8 @@ Value* FormatFn(const char* name, State* state, const std::vector(mke2fs_argv)); if (status != 0) { - LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location - << ", falling back to make_ext4fs"; - status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle); - if (status != 0) { - LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location; - return StringValue(""); - } - return StringValue(location); + LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location; + return StringValue(""); } const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-a", mount_point.c_str(), -- cgit v1.2.3 From a88cc5440ed79a0927f57bbd03377ff3ce04a760 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 25 Oct 2017 13:16:54 -0700 Subject: Switch to bionic gtest in bootable/recovery We encountered segfaults in Imgdiff host tests due to the failure to reset states of getopt. The problem can be solved by switching to use bionic's gtest where a new process is forked for each test. Also modify the recovery_component_test to make sure it runs in parallel. Changes include: 1. Merge the writes to misc partition into one single test. 2. Change the hard coded location "/cache/saved.file" into a configurable variable. Bug: 67849209 Test: recovery tests pass Change-Id: I165d313f32b83393fb7922c5078636ac40b50bc2 --- applypatch/applypatch.cpp | 18 +-- applypatch/freecache.cpp | 7 +- applypatch/include/applypatch/applypatch.h | 11 +- bootloader_message/bootloader_message.cpp | 33 ++--- .../bootloader_message/bootloader_message.h | 5 + minadbd/Android.mk | 9 +- tests/Android.mk | 10 +- tests/component/applypatch_test.cpp | 131 +++++++------------ tests/component/bootloader_message_test.cpp | 140 ++++----------------- tests/component/uncrypt_test.cpp | 99 +++++++++------ 10 files changed, 188 insertions(+), 275 deletions(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index c8b75df79..2153b5f19 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -42,6 +42,8 @@ #include "otafault/ota_io.h" #include "otautil/print_sha1.h" +std::string cache_temp_source = "/cache/saved.file"; + static int LoadPartitionContents(const std::string& filename, FileContents* file); static size_t FileSink(const unsigned char* data, size_t len, int fd); static int GenerateTarget(const FileContents& source_file, const std::unique_ptr& patch, @@ -411,12 +413,10 @@ int applypatch_check(const char* filename, const std::vector& patch (!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) { printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename); - // If the source file is missing or corrupted, it might be because - // we were killed in the middle of patching it. A copy of it - // should have been made in CACHE_TEMP_SOURCE. If that file - // exists and matches the sha1 we're looking for, the check still - // passes. - if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) { + // If the source file is missing or corrupted, it might be because we were killed in the middle + // of patching it. A copy of it should have been made in cache_temp_source. If that file + // exists and matches the sha1 we're looking for, the check still passes. + if (LoadFileContents(cache_temp_source.c_str(), &file) != 0) { printf("failed to load cache file\n"); return 1; } @@ -539,7 +539,7 @@ int applypatch(const char* source_filename, const char* target_filename, printf("source file is bad; trying copy\n"); FileContents copy_file; - if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) { + if (LoadFileContents(cache_temp_source.c_str(), ©_file) < 0) { printf("failed to read copy file\n"); return 1; } @@ -634,7 +634,7 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr printf("not enough free space on /cache\n"); return 1; } - if (SaveFileContents(CACHE_TEMP_SOURCE, &source_file) < 0) { + if (SaveFileContents(cache_temp_source.c_str(), &source_file) < 0) { printf("failed to back up source file\n"); return 1; } @@ -680,7 +680,7 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr } // Delete the backup copy of the source. - unlink(CACHE_TEMP_SOURCE); + unlink(cache_temp_source.c_str()); // Success! return 0; diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp index 331cae265..0a40baa97 100644 --- a/applypatch/freecache.cpp +++ b/applypatch/freecache.cpp @@ -90,10 +90,9 @@ static std::set FindExpendableFiles() { while ((de = readdir(d.get())) != 0) { std::string path = std::string(dirs[i]) + "/" + de->d_name; - // We can't delete CACHE_TEMP_SOURCE; if it's there we might have - // restarted during installation and could be depending on it to - // be there. - if (path == CACHE_TEMP_SOURCE) { + // We can't delete cache_temp_source; if it's there we might have restarted during + // installation and could be depending on it to be there. + if (path == cache_temp_source) { continue; } diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 2a3b3ef39..bcb8a4126 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -36,12 +36,11 @@ struct FileContents { struct stat st; }; -// When there isn't enough room on the target filesystem to hold the -// patched version of the file, we copy the original here and delete -// it to free up space. If the expected source file doesn't exist, or -// is corrupted, we look to see if this file contains the bits we want -// and use it as the source instead. -#define CACHE_TEMP_SOURCE "/cache/saved.file" +// When there isn't enough room on the target filesystem to hold the patched version of the file, +// we copy the original here and delete it to free up space. If the expected source file doesn't +// exist, or is corrupted, we look to see if the cached file contains the bits we want and use it as +// the source instead. The default location for the cached source is "/cache/saved.file". +extern std::string cache_temp_source; using SinkFn = std::function; diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index f91446b43..aaeffdc5c 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -159,14 +159,8 @@ bool clear_bootloader_message(std::string* err) { bool write_bootloader_message(const std::vector& options, std::string* err) { bootloader_message boot = {}; - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); - for (const auto& s : options) { - strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery)); - if (s.back() != '\n') { - strlcat(boot.recovery, "\n", sizeof(boot.recovery)); - } - } + update_bootloader_message_in_struct(&boot, options); + return write_bootloader_message(boot, err); } @@ -175,20 +169,27 @@ bool update_bootloader_message(const std::vector& options, std::str if (!read_bootloader_message(&boot, err)) { return false; } + update_bootloader_message_in_struct(&boot, options); - // Zero out the entire fields. - memset(boot.command, 0, sizeof(boot.command)); - memset(boot.recovery, 0, sizeof(boot.recovery)); + return write_bootloader_message(boot, err); +} + +bool update_bootloader_message_in_struct(bootloader_message* boot, + const std::vector& options) { + if (!boot) return false; + // Replace the command & recovery fields. + memset(boot->command, 0, sizeof(boot->command)); + memset(boot->recovery, 0, sizeof(boot->recovery)); - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); + strlcpy(boot->command, "boot-recovery", sizeof(boot->command)); + strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery)); for (const auto& s : options) { - strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery)); + strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery)); if (s.back() != '\n') { - strlcat(boot.recovery, "\n", sizeof(boot.recovery)); + strlcat(boot->recovery, "\n", sizeof(boot->recovery)); } } - return write_bootloader_message(boot, err); + return true; } bool write_reboot_bootloader(std::string* err) { diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 2ffbfc9e3..798f3bb8c 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -207,6 +207,11 @@ bool write_bootloader_message(const std::vector& options, std::stri // only update the command and recovery fields. bool update_bootloader_message(const std::vector& options, std::string* err); +// Update bootloader message (boots into recovery with the |options|) in |boot|. Will only update +// the command and recovery fields. +bool update_bootloader_message_in_struct(bootloader_message* boot, + const std::vector& options); + // Clear BCB. bool clear_bootloader_message(std::string* err); diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 803171d99..3c9ab3a7f 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -46,7 +46,12 @@ LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_SRC_FILES := fuse_adb_provider_test.cpp LOCAL_CFLAGS := $(minadbd_cflags) LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb -LOCAL_STATIC_LIBRARIES := libminadbd -LOCAL_SHARED_LIBRARIES := liblog libbase libcutils +LOCAL_STATIC_LIBRARIES := \ + libBionicGtestMain \ + libminadbd +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libbase \ + libcutils include $(BUILD_NATIVE_TEST) diff --git a/tests/Android.mk b/tests/Android.mk index 8ebb60308..d911c25e4 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -30,7 +30,8 @@ LOCAL_STATIC_LIBRARIES := \ libutils \ libz \ libselinux \ - libbase + libbase \ + libBionicGtestMain LOCAL_SRC_FILES := \ unit/asn1_decoder_test.cpp \ @@ -50,7 +51,8 @@ LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE := recovery_manual_test LOCAL_STATIC_LIBRARIES := \ libminui \ - libbase + libbase \ + libBionicGtestMain LOCAL_SRC_FILES := manual/recovery_test.cpp LOCAL_SHARED_LIBRARIES := \ @@ -163,6 +165,7 @@ LOCAL_STATIC_LIBRARIES := \ libsquashfs_utils \ libcutils \ libbrotli \ + libBionicGtestMain \ $(tune2fs_static_libraries) testdata_files := $(call find-subdir-files, testdata/*) @@ -212,7 +215,8 @@ LOCAL_STATIC_LIBRARIES := \ libbz \ libdivsufsort64 \ libdivsufsort \ - libz + libz \ + libBionicGtestMain LOCAL_SHARED_LIBRARIES := \ liblog include $(BUILD_HOST_NATIVE_TEST) diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 15ec08fe7..21c9a52dc 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -53,8 +53,7 @@ static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = } static void mangle_file(const std::string& fname) { - std::string content; - content.reserve(1024); + std::string content(1024, '\0'); for (size_t i = 0; i < 1024; i++) { content[i] = rand() % 256; } @@ -63,16 +62,11 @@ static void mangle_file(const std::string& fname) { class ApplyPatchTest : public ::testing::Test { public: - static void SetUpTestCase() { + virtual void SetUp() override { // set up files old_file = from_testdata_base("old.file"); new_file = from_testdata_base("new.file"); - patch_file = from_testdata_base("patch.bsdiff"); - rand_file = "/cache/applypatch_test_rand.file"; - cache_file = "/cache/saved.file"; - - // write stuff to rand_file - ASSERT_TRUE(android::base::WriteStringToFile("hello", rand_file)); + nonexistent_file = from_testdata_base("nonexistent.file"); // set up SHA constants sha1sum(old_file, &old_sha1, &old_size); @@ -82,56 +76,35 @@ class ApplyPatchTest : public ::testing::Test { bad_sha1_b = android::base::StringPrintf("%040x", rand()); } - static std::string old_file; - static std::string new_file; - static std::string rand_file; - static std::string cache_file; - static std::string patch_file; + std::string old_file; + std::string new_file; + std::string nonexistent_file; - static std::string old_sha1; - static std::string new_sha1; - static std::string bad_sha1_a; - static std::string bad_sha1_b; + std::string old_sha1; + std::string new_sha1; + std::string bad_sha1_a; + std::string bad_sha1_b; - static size_t old_size; - static size_t new_size; + size_t old_size; + size_t new_size; }; -static void cp(const std::string& src, const std::string& tgt) { - std::string cmd = "cp " + src + " " + tgt; - system(cmd.c_str()); -} - -static void backup_old() { - cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file); -} - -static void restore_old() { - cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file); -} - class ApplyPatchCacheTest : public ApplyPatchTest { - public: - virtual void SetUp() { - backup_old(); + protected: + void SetUp() override { + ApplyPatchTest::SetUp(); + cache_temp_source = old_file; } +}; - virtual void TearDown() { - restore_old(); +class ApplyPatchModesTest : public ::testing::Test { + protected: + void SetUp() override { + cache_temp_source = cache_source.path; } -}; -std::string ApplyPatchTest::old_file; -std::string ApplyPatchTest::new_file; -std::string ApplyPatchTest::rand_file; -std::string ApplyPatchTest::patch_file; -std::string ApplyPatchTest::cache_file; -std::string ApplyPatchTest::old_sha1; -std::string ApplyPatchTest::new_sha1; -std::string ApplyPatchTest::bad_sha1_a; -std::string ApplyPatchTest::bad_sha1_b; -size_t ApplyPatchTest::old_size; -size_t ApplyPatchTest::new_size; + TemporaryFile cache_source; +}; TEST_F(ApplyPatchTest, CheckModeSkip) { std::vector sha1s; @@ -189,43 +162,31 @@ TEST_F(ApplyPatchTest, CheckModeEmmcTarget) { ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s)); } -TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) { - mangle_file(old_file); - std::vector sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceSingle) { + TemporaryFile temp_file; + mangle_file(temp_file.path); + std::vector sha1s_single = { old_sha1 }; + ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_single)); + ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_single)); } -TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) { - mangle_file(old_file); - std::vector sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceMultiple) { + TemporaryFile temp_file; + mangle_file(temp_file.path); + std::vector sha1s_multiple = { bad_sha1_a, old_sha1, bad_sha1_b }; + ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_multiple)); + ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_multiple)); } -TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) { - mangle_file(old_file); - std::vector sha1s = { bad_sha1_a, bad_sha1_b }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); -} - -TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) { - unlink(&old_file[0]); - std::vector sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); -} - -TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) { - unlink(&old_file[0]); - std::vector sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); -} - -TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) { - unlink(&old_file[0]); - std::vector sha1s = { bad_sha1_a, bad_sha1_b }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceFailure) { + TemporaryFile temp_file; + mangle_file(temp_file.path); + std::vector sha1s_failure = { bad_sha1_a, bad_sha1_b }; + ASSERT_NE(0, applypatch_check(temp_file.path, sha1s_failure)); + ASSERT_NE(0, applypatch_check(nonexistent_file.c_str(), sha1s_failure)); } -TEST(ApplyPatchModesTest, InvalidArgs) { +TEST_F(ApplyPatchModesTest, InvalidArgs) { // At least two args (including the filename). ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" })); @@ -233,7 +194,7 @@ TEST(ApplyPatchModesTest, InvalidArgs) { ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" })); } -TEST(ApplyPatchModesTest, PatchModeEmmcTarget) { +TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) { std::string boot_img = from_testdata_base("boot.img"); size_t boot_img_size; std::string boot_img_sha1; @@ -303,7 +264,7 @@ TEST(ApplyPatchModesTest, PatchModeEmmcTarget) { ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data())); } -TEST(ApplyPatchModesTest, PatchModeInvalidArgs) { +TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) { // Invalid bonus file. ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" })); @@ -364,11 +325,11 @@ TEST(ApplyPatchModesTest, PatchModeInvalidArgs) { ASSERT_NE(0, applypatch_modes(args6.size(), args6.data())); } -TEST(ApplyPatchModesTest, CheckModeInvalidArgs) { +TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) { // Insufficient args. ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" })); } -TEST(ApplyPatchModesTest, ShowLicenses) { +TEST_F(ApplyPatchModesTest, ShowLicenses) { ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" })); } diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp index b38bc7134..6cc59a495 100644 --- a/tests/component/bootloader_message_test.cpp +++ b/tests/component/bootloader_message_test.cpp @@ -18,53 +18,12 @@ #include #include +#include #include #include -class BootloaderMessageTest : public ::testing::Test { - protected: - BootloaderMessageTest() : has_misc(true) {} - - virtual void SetUp() override { - std::string err; - has_misc = !get_bootloader_message_blk_device(&err).empty(); - } - - virtual void TearDown() override { - // Clear the BCB. - if (has_misc) { - std::string err; - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; - } - } - - bool has_misc; -}; - -TEST_F(BootloaderMessageTest, clear_bootloader_message) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - - // Clear the BCB. - std::string err; - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; - - // Verify the content. - bootloader_message boot; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; - - // All the bytes should be cleared. - ASSERT_EQ(std::string(sizeof(boot), '\0'), - std::string(reinterpret_cast(&boot), sizeof(boot))); -} - -TEST_F(BootloaderMessageTest, read_and_write_bootloader_message) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } +TEST(BootloaderMessageTest, read_and_write_bootloader_message) { + TemporaryFile temp_misc; // Write the BCB. bootloader_message boot = {}; @@ -73,90 +32,71 @@ TEST_F(BootloaderMessageTest, read_and_write_bootloader_message) { strlcpy(boot.status, "status1", sizeof(boot.status)); std::string err; - ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err; + ASSERT_TRUE(write_bootloader_message_to(boot, temp_misc.path, &err)) + << "Failed to write BCB: " << err; // Read and verify. bootloader_message boot_verify; - ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err; + ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_misc.path, &err)) + << "Failed to read BCB: " << err; ASSERT_EQ(std::string(reinterpret_cast(&boot), sizeof(boot)), std::string(reinterpret_cast(&boot_verify), sizeof(boot_verify))); } -TEST_F(BootloaderMessageTest, write_bootloader_message_options) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - +TEST(BootloaderMessageTest, update_bootloader_message_in_struct) { // Write the options to BCB. std::vector options = { "option1", "option2" }; - std::string err; - ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err; - // Inject some bytes into boot, which should be overwritten while reading. - bootloader_message boot; + bootloader_message boot = {}; + // Inject some bytes into boot. strlcpy(boot.recovery, "random message", sizeof(boot.recovery)); + strlcpy(boot.status, "status bytes", sizeof(boot.status)); + strlcpy(boot.stage, "stage bytes", sizeof(boot.stage)); strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved)); - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); // Verify that command and recovery fields should be set. ASSERT_EQ("boot-recovery", std::string(boot.command)); std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n"; ASSERT_EQ(expected, std::string(boot.recovery)); - // The rest should be cleared. - ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status))); - ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage))); - ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), - std::string(boot.reserved, sizeof(boot.reserved))); + // The rest should be intact. + ASSERT_EQ("status bytes", std::string(boot.status)); + ASSERT_EQ("stage bytes", std::string(boot.stage)); + ASSERT_EQ("reserved bytes", std::string(boot.reserved)); } -TEST_F(BootloaderMessageTest, write_bootloader_message_options_empty) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - +TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_empty) { // Write empty vector. std::vector options; - std::string err; - ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err; // Read and verify. - bootloader_message boot; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + bootloader_message boot = {}; + ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); // command and recovery fields should be set. ASSERT_EQ("boot-recovery", std::string(boot.command)); ASSERT_EQ("recovery\n", std::string(boot.recovery)); - // The rest should be cleared. + // The rest should be empty. ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status))); ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage))); ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), std::string(boot.reserved, sizeof(boot.reserved))); } -TEST_F(BootloaderMessageTest, write_bootloader_message_options_long) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - +TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) { // Write super long message. std::vector options; for (int i = 0; i < 100; i++) { options.push_back("option: " + std::to_string(i)); } - std::string err; - ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err; - // Read and verify. - bootloader_message boot; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + bootloader_message boot = {}; + ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); // Make sure it's long enough. std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n"; @@ -167,40 +107,10 @@ TEST_F(BootloaderMessageTest, write_bootloader_message_options_long) { ASSERT_EQ(expected.substr(0, sizeof(boot.recovery) - 1), std::string(boot.recovery)); ASSERT_EQ('\0', boot.recovery[sizeof(boot.recovery) - 1]); - // The rest should be cleared. + // The rest should be empty. ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status))); ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage))); ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), std::string(boot.reserved, sizeof(boot.reserved))); } -TEST_F(BootloaderMessageTest, update_bootloader_message) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - - // Inject some bytes into boot, which should be not overwritten later. - bootloader_message boot; - strlcpy(boot.recovery, "random message", sizeof(boot.recovery)); - strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved)); - std::string err; - ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err; - - // Update the BCB message. - std::vector options = { "option1", "option2" }; - ASSERT_TRUE(update_bootloader_message(options, &err)) << "Failed to update BCB: " << err; - - bootloader_message boot_verify; - ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err; - - // Verify that command and recovery fields should be set. - ASSERT_EQ("boot-recovery", std::string(boot_verify.command)); - std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n"; - ASSERT_EQ(expected, std::string(boot_verify.recovery)); - - // The rest should be intact. - ASSERT_EQ(std::string(boot.status), std::string(boot_verify.status)); - ASSERT_EQ(std::string(boot.stage), std::string(boot_verify.stage)); - ASSERT_EQ(std::string(boot.reserved), std::string(boot_verify.reserved)); -} diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp index 3925236a5..55baca2e3 100644 --- a/tests/component/uncrypt_test.cpp +++ b/tests/component/uncrypt_test.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -38,43 +39,49 @@ static const std::string INIT_SVC_CLEAR_BCB = "init.svc.clear-bcb"; static const std::string INIT_SVC_UNCRYPT = "init.svc.uncrypt"; static constexpr int SOCKET_CONNECTION_MAX_RETRY = 30; +static void StopService() { + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt")); + + bool success = false; + for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { + std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, ""); + std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, ""); + std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, ""); + GTEST_LOG_(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb + << "] uncrypt: [" << uncrypt << "]"; + if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") { + success = true; + break; + } + sleep(1); + } + + ASSERT_TRUE(success) << "uncrypt service is not available."; +} + class UncryptTest : public ::testing::Test { protected: UncryptTest() : has_misc(true) {} - virtual void SetUp() override { - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt")); - - bool success = false; - for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { - std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, ""); - std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, ""); - std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, ""); - LOG(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb << "] uncrypt: [" - << uncrypt << "]"; - if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") { - success = true; - break; - } - sleep(1); - } - - ASSERT_TRUE(success) << "uncrypt service is not available."; - + void SetUp() override { std::string err; has_misc = !get_bootloader_message_blk_device(&err).empty(); } - void SetupOrClearBcb(bool isSetup, const std::string& message, - const std::string& message_in_bcb) const { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; + void TearDown() override { + // Clear the BCB. + if (has_misc) { + std::string err; + ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; } + } - // Trigger the setup-bcb service. + void SetupOrClearBcb(bool isSetup, const std::string& message, + const std::string& message_in_bcb) const { + // Restart the setup-bcb service. + StopService(); ASSERT_TRUE(android::base::SetProperty("ctl.start", isSetup ? "setup-bcb" : "clear-bcb")); // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). @@ -144,27 +151,49 @@ class UncryptTest : public ::testing::Test { } } + void VerifyBootloaderMessage(const std::string& expected) { + std::string err; + bootloader_message boot; + ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + + // Check that we have all the expected bytes. + ASSERT_EQ(expected, std::string(reinterpret_cast(&boot), sizeof(boot))); + } + bool has_misc; }; TEST_F(UncryptTest, setup_bcb) { + if (!has_misc) { + GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; + return; + } + + std::string random_data; + random_data.reserve(sizeof(bootloader_message)); + generate_n(back_inserter(random_data), sizeof(bootloader_message), []() { return rand() % 128; }); + + bootloader_message boot; + memcpy(&boot, random_data.c_str(), random_data.size()); + + std::string err; + ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err; + VerifyBootloaderMessage(random_data); + + ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; + VerifyBootloaderMessage(std::string(sizeof(bootloader_message), '\0')); + std::string message = "--update_message=abc value"; std::string message_in_bcb = "recovery\n--update_message=abc value\n"; SetupOrClearBcb(true, message, message_in_bcb); -} -TEST_F(UncryptTest, clear_bcb) { SetupOrClearBcb(false, "", ""); -} -TEST_F(UncryptTest, setup_bcb_wipe_ab) { TemporaryFile wipe_package; ASSERT_TRUE(android::base::WriteStringToFile(std::string(345, 'a'), wipe_package.path)); // It's expected to store a wipe package in /misc, with the package size passed to recovery. - std::string message = - "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s; - std::string message_in_bcb = - "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n"; + message = "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s; + message_in_bcb = "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n"; SetupOrClearBcb(true, message, message_in_bcb); } -- cgit v1.2.3 From 3d843868aef444275f4d9aa093c12d51f9a89c00 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 3 Nov 2017 15:52:22 -0700 Subject: Add libbrotli as a dependency when building applypatch binary libbrotli is now used in libbspatch. Bug: 34220646 Test: mma Change-Id: I8d3527141539ffdae112d9042355fd705caeb250 --- applypatch/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/applypatch/Android.bp b/applypatch/Android.bp index 4d2d4b76a..b37614072 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -99,6 +99,7 @@ cc_binary { shared_libs: [ "libbase", + "libbrotli", "libbz", "libcrypto", "liblog", -- cgit v1.2.3 From 91a7aa4577feb5f80038a30d15765e8b79845cf1 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 1 May 2017 15:57:38 -0700 Subject: Clean up fuse_sideload and add a testcase. This CL mainly changes: a) moving the interface in struct provider_vtab to std::function; b) code cleanup, such as moving the declaration closer to the uses, using explicit type conversion. Test: recovery_component_test Test: minadbd_test Test: Sideload a package on marlin. Change-Id: Id0e3c70f1ada54a4cd985b54c84438c23ed4687e --- fuse_sdcard_provider.cpp | 88 +++--- fuse_sideload.cpp | 545 +++++++++++++++++-------------------- fuse_sideload.h | 26 +- minadbd/fuse_adb_provider.cpp | 53 ++-- minadbd/fuse_adb_provider.h | 8 +- minadbd/fuse_adb_provider_test.cpp | 6 +- tests/component/sideload_test.cpp | 70 ++++- 7 files changed, 409 insertions(+), 387 deletions(-) diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp index b0ecf96be..46bdf1774 100644 --- a/fuse_sdcard_provider.cpp +++ b/fuse_sdcard_provider.cpp @@ -14,72 +14,70 @@ * limitations under the License. */ -#include +#include "fuse_sdcard_provider.h" + +#include +#include #include +#include #include -#include #include #include #include -#include + +#include #include #include "fuse_sideload.h" struct file_data { - int fd; // the underlying sdcard file + int fd; // the underlying sdcard file - uint64_t file_size; - uint32_t block_size; + uint64_t file_size; + uint32_t block_size; }; -static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { - file_data* fd = reinterpret_cast(cookie); - - off64_t offset = ((off64_t) block) * fd->block_size; - if (TEMP_FAILURE_RETRY(lseek64(fd->fd, offset, SEEK_SET)) == -1) { - fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno)); - return -EIO; - } +static int read_block_file(const file_data& fd, uint32_t block, uint8_t* buffer, + uint32_t fetch_size) { + off64_t offset = static_cast(block) * fd.block_size; + if (TEMP_FAILURE_RETRY(lseek64(fd.fd, offset, SEEK_SET)) == -1) { + fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno)); + return -EIO; + } - if (!android::base::ReadFully(fd->fd, buffer, fetch_size)) { - fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno)); - return -EIO; - } - - return 0; -} + if (!android::base::ReadFully(fd.fd, buffer, fetch_size)) { + fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno)); + return -EIO; + } -static void close_file(void* cookie) { - file_data* fd = reinterpret_cast(cookie); - close(fd->fd); + return 0; } bool start_sdcard_fuse(const char* path) { - struct stat sb; - if (stat(path, &sb) == -1) { - fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno)); - return false; - } + struct stat sb; + if (stat(path, &sb) == -1) { + fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno)); + return false; + } - file_data fd; - fd.fd = open(path, O_RDONLY); - if (fd.fd == -1) { - fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); - return false; - } - fd.file_size = sb.st_size; - fd.block_size = 65536; + file_data fd; + fd.fd = open(path, O_RDONLY); + if (fd.fd == -1) { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return false; + } + fd.file_size = sb.st_size; + fd.block_size = 65536; - provider_vtab vtab; - vtab.read_block = read_block_file; - vtab.close = close_file; + provider_vtab vtab; + vtab.read_block = std::bind(&read_block_file, fd, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3); + vtab.close = [&fd]() { close(fd.fd); }; - // The installation process expects to find the sdcard unmounted. - // Unmount it with MNT_DETACH so that our open file continues to - // work but new references see it as unmounted. - umount2("/sdcard", MNT_DETACH); + // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so + // that our open file continues to work but new references see it as unmounted. + umount2("/sdcard", MNT_DETACH); - return run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size) == 0; + return run_fuse_sideload(vtab, fd.file_size, fd.block_size) == 0; } diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp index 219374fdb..1c7e98f01 100644 --- a/fuse_sideload.cpp +++ b/fuse_sideload.cpp @@ -41,337 +41,310 @@ // two files is implemented. In particular, you can't opendir() or // readdir() on the "/sideload" directory; ls on it won't work. -#include -#include +#include "fuse_sideload.h" + #include #include -#include +#include // PATH_MAX #include -#include +#include #include #include #include -#include #include -#include -#include +#include // MIN #include -#include -#include #include #include +#include #include +#include #include +#include #include -#include "fuse_sideload.h" +static constexpr uint64_t PACKAGE_FILE_ID = FUSE_ROOT_ID + 1; +static constexpr uint64_t EXIT_FLAG_ID = FUSE_ROOT_ID + 2; -#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1) -#define EXIT_FLAG_ID (FUSE_ROOT_ID+2) +static constexpr int NO_STATUS = 1; +static constexpr int NO_STATUS_EXIT = 2; -#define NO_STATUS 1 -#define NO_STATUS_EXIT 2 +using SHA256Digest = std::array; struct fuse_data { - int ffd; // file descriptor for the fuse socket + android::base::unique_fd ffd; // file descriptor for the fuse socket - struct provider_vtab* vtab; - void* cookie; + provider_vtab vtab; - uint64_t file_size; // bytes + uint64_t file_size; // bytes - uint32_t block_size; // block size that the adb host is using to send the file to us - uint32_t file_blocks; // file size in block_size blocks + uint32_t block_size; // block size that the adb host is using to send the file to us + uint32_t file_blocks; // file size in block_size blocks - uid_t uid; - gid_t gid; + uid_t uid; + gid_t gid; - uint32_t curr_block; // cache the block most recently read from the host - uint8_t* block_data; + uint32_t curr_block; // cache the block most recently read from the host + uint8_t* block_data; - uint8_t* extra_block; // another block of storage for reads that - // span two blocks + uint8_t* extra_block; // another block of storage for reads that span two blocks - uint8_t* hashes; // SHA-256 hash of each block (all zeros - // if block hasn't been read yet) + std::vector + hashes; // SHA-256 hash of each block (all zeros if block hasn't been read yet) }; -static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len) -{ - struct fuse_out_header hdr; - struct iovec vec[2]; - int res; - - hdr.len = len + sizeof(hdr); - hdr.error = 0; - hdr.unique = unique; - - vec[0].iov_base = &hdr; - vec[0].iov_len = sizeof(hdr); - vec[1].iov_base = /* const_cast */(void*)(data); - vec[1].iov_len = len; - - res = writev(fd->ffd, vec, 2); - if (res < 0) { - printf("*** REPLY FAILED *** %s\n", strerror(errno)); - } +static void fuse_reply(const fuse_data* fd, uint64_t unique, const void* data, size_t len) { + fuse_out_header hdr; + hdr.len = len + sizeof(hdr); + hdr.error = 0; + hdr.unique = unique; + + struct iovec vec[2]; + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = const_cast(data); + vec[1].iov_len = len; + + int res = writev(fd->ffd, vec, 2); + if (res == -1) { + printf("*** REPLY FAILED *** %s\n", strerror(errno)); + } } -static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_init_in* req = reinterpret_cast(data); - struct fuse_init_out out; - size_t fuse_struct_size; - - - /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out - * defined (fuse version 7.6). The structure is the same from 7.6 through - * 7.22. Beginning with 7.23, the structure increased in size and added - * new parameters. - */ - if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) { - printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6", - req->major, req->minor, FUSE_KERNEL_VERSION); - return -1; - } +static int handle_init(void* data, fuse_data* fd, const fuse_in_header* hdr) { + const fuse_init_in* req = static_cast(data); + + // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out defined (fuse version 7.6). + // The structure is the same from 7.6 through 7.22. Beginning with 7.23, the structure increased + // in size and added new parameters. + if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) { + printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6", req->major, + req->minor, FUSE_KERNEL_VERSION); + return -1; + } - out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION); - fuse_struct_size = sizeof(out); + fuse_init_out out; + out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION); + size_t fuse_struct_size = sizeof(out); #if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) - /* FUSE_KERNEL_VERSION >= 23. */ + /* FUSE_KERNEL_VERSION >= 23. */ - /* If the kernel only works on minor revs older than or equal to 22, - * then use the older structure size since this code only uses the 7.22 - * version of the structure. */ - if (req->minor <= 22) { - fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE; - } + // If the kernel only works on minor revs older than or equal to 22, then use the older structure + // size since this code only uses the 7.22 version of the structure. + if (req->minor <= 22) { + fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE; + } #endif - out.major = FUSE_KERNEL_VERSION; - out.max_readahead = req->max_readahead; - out.flags = 0; - out.max_background = 32; - out.congestion_threshold = 32; - out.max_write = 4096; - fuse_reply(fd, hdr->unique, &out, fuse_struct_size); + out.major = FUSE_KERNEL_VERSION; + out.max_readahead = req->max_readahead; + out.flags = 0; + out.max_background = 32; + out.congestion_threshold = 32; + out.max_write = 4096; + fuse_reply(fd, hdr->unique, &out, fuse_struct_size); - return NO_STATUS; + return NO_STATUS; } -static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd, - uint64_t nodeid, uint64_t size, uint32_t mode) { - memset(attr, 0, sizeof(*attr)); - attr->nlink = 1; - attr->uid = fd->uid; - attr->gid = fd->gid; - attr->blksize = 4096; - - attr->ino = nodeid; - attr->size = size; - attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1); - attr->mode = mode; +static void fill_attr(fuse_attr* attr, const fuse_data* fd, uint64_t nodeid, uint64_t size, + uint32_t mode) { + *attr = {}; + attr->nlink = 1; + attr->uid = fd->uid; + attr->gid = fd->gid; + attr->blksize = 4096; + + attr->ino = nodeid; + attr->size = size; + attr->blocks = (size == 0) ? 0 : (((size - 1) / attr->blksize) + 1); + attr->mode = mode; } -static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { - struct fuse_attr_out out; - memset(&out, 0, sizeof(out)); - out.attr_valid = 10; - - if (hdr->nodeid == FUSE_ROOT_ID) { - fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); - } else if (hdr->nodeid == PACKAGE_FILE_ID) { - fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (hdr->nodeid == EXIT_FLAG_ID) { - fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); - } else { - return -ENOENT; - } +static int handle_getattr(void* /* data */, const fuse_data* fd, const fuse_in_header* hdr) { + fuse_attr_out out = {}; + out.attr_valid = 10; + + if (hdr->nodeid == FUSE_ROOT_ID) { + fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); + } else if (hdr->nodeid == PACKAGE_FILE_ID) { + fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); + } else if (hdr->nodeid == EXIT_FLAG_ID) { + fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); + } else { + return -ENOENT; + } - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; } -static int handle_lookup(void* data, struct fuse_data* fd, - const struct fuse_in_header* hdr) { - struct fuse_entry_out out; - memset(&out, 0, sizeof(out)); - out.entry_valid = 10; - out.attr_valid = 10; - - if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, reinterpret_cast(data), - sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) { - out.nodeid = PACKAGE_FILE_ID; - out.generation = PACKAGE_FILE_ID; - fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, reinterpret_cast(data), - sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) { - out.nodeid = EXIT_FLAG_ID; - out.generation = EXIT_FLAG_ID; - fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); - } else { - return -ENOENT; - } +static int handle_lookup(void* data, const fuse_data* fd, const fuse_in_header* hdr) { + if (data == nullptr) return -ENOENT; + + fuse_entry_out out = {}; + out.entry_valid = 10; + out.attr_valid = 10; + + std::string filename(static_cast(data)); + if (filename == FUSE_SIDELOAD_HOST_FILENAME) { + out.nodeid = PACKAGE_FILE_ID; + out.generation = PACKAGE_FILE_ID; + fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); + } else if (filename == FUSE_SIDELOAD_HOST_EXIT_FLAG) { + out.nodeid = EXIT_FLAG_ID; + out.generation = EXIT_FLAG_ID; + fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); + } else { + return -ENOENT; + } - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; } -static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { - if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; - if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; +static int handle_open(void* /* data */, const fuse_data* fd, const fuse_in_header* hdr) { + if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; + if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; - struct fuse_open_out out; - memset(&out, 0, sizeof(out)); - out.fh = 10; // an arbitrary number; we always use the same handle - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return NO_STATUS; + fuse_open_out out = {}; + out.fh = 10; // an arbitrary number; we always use the same handle + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return NO_STATUS; } -static int handle_flush(void* /* data */, struct fuse_data* /* fd */, - const struct fuse_in_header* /* hdr */) { - return 0; +static int handle_flush(void* /* data */, fuse_data* /* fd */, const fuse_in_header* /* hdr */) { + return 0; } -static int handle_release(void* /* data */, struct fuse_data* /* fd */, - const struct fuse_in_header* /* hdr */) { - return 0; +static int handle_release(void* /* data */, fuse_data* /* fd */, const fuse_in_header* /* hdr */) { + return 0; } // Fetch a block from the host into fd->curr_block and fd->block_data. // Returns 0 on successful fetch, negative otherwise. -static int fetch_block(struct fuse_data* fd, uint32_t block) { - if (block == fd->curr_block) { - return 0; - } +static int fetch_block(fuse_data* fd, uint32_t block) { + if (block == fd->curr_block) { + return 0; + } - if (block >= fd->file_blocks) { - memset(fd->block_data, 0, fd->block_size); - fd->curr_block = block; - return 0; - } + if (block >= fd->file_blocks) { + memset(fd->block_data, 0, fd->block_size); + fd->curr_block = block; + return 0; + } - size_t fetch_size = fd->block_size; - if (block * fd->block_size + fetch_size > fd->file_size) { - // If we're reading the last (partial) block of the file, - // expect a shorter response from the host, and pad the rest - // of the block with zeroes. - fetch_size = fd->file_size - (block * fd->block_size); - memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size); - } + size_t fetch_size = fd->block_size; + if (block * fd->block_size + fetch_size > fd->file_size) { + // If we're reading the last (partial) block of the file, expect a shorter response from the + // host, and pad the rest of the block with zeroes. + fetch_size = fd->file_size - (block * fd->block_size); + memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size); + } - int result = fd->vtab->read_block(fd->cookie, block, fd->block_data, fetch_size); - if (result < 0) return result; + int result = fd->vtab.read_block(block, fd->block_data, fetch_size); + if (result < 0) return result; - fd->curr_block = block; + fd->curr_block = block; - // Verify the hash of the block we just got from the host. - // - // - If the hash of the just-received data matches the stored hash - // for the block, accept it. - // - If the stored hash is all zeroes, store the new hash and - // accept the block (this is the first time we've read this - // block). - // - Otherwise, return -EINVAL for the read. - - uint8_t hash[SHA256_DIGEST_LENGTH]; - SHA256(fd->block_data, fd->block_size, hash); - uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_LENGTH; - if (memcmp(hash, blockhash, SHA256_DIGEST_LENGTH) == 0) { - return 0; - } + // Verify the hash of the block we just got from the host. + // + // - If the hash of the just-received data matches the stored hash for the block, accept it. + // - If the stored hash is all zeroes, store the new hash and accept the block (this is the first + // time we've read this block). + // - Otherwise, return -EINVAL for the read. - int i; - for (i = 0; i < SHA256_DIGEST_LENGTH; ++i) { - if (blockhash[i] != 0) { - fd->curr_block = -1; - return -EIO; - } - } + SHA256Digest hash; + SHA256(fd->block_data, fd->block_size, hash.data()); - memcpy(blockhash, hash, SHA256_DIGEST_LENGTH); + const SHA256Digest& blockhash = fd->hashes[block]; + if (hash == blockhash) { return 0; + } + + for (uint8_t i : blockhash) { + if (i != 0) { + fd->curr_block = -1; + return -EIO; + } + } + + fd->hashes[block] = hash; + return 0; } -static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_read_in* req = reinterpret_cast(data); - struct fuse_out_header outhdr; - struct iovec vec[3]; - int vec_used; - int result; - - if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; - - uint64_t offset = req->offset; - uint32_t size = req->size; - - // The docs on the fuse kernel interface are vague about what to - // do when a read request extends past the end of the file. We - // can return a short read -- the return structure does include a - // length field -- but in testing that caused the program using - // the file to segfault. (I speculate that this is due to the - // reading program accessing it via mmap; maybe mmap dislikes when - // you return something short of a whole page?) To fix this we - // zero-pad reads that extend past the end of the file so we're - // always returning exactly as many bytes as were requested. - // (Users of the mapped file have to know its real length anyway.) - - outhdr.len = sizeof(outhdr) + size; - outhdr.error = 0; - outhdr.unique = hdr->unique; - vec[0].iov_base = &outhdr; - vec[0].iov_len = sizeof(outhdr); - - uint32_t block = offset / fd->block_size; - result = fetch_block(fd, block); +static int handle_read(void* data, fuse_data* fd, const fuse_in_header* hdr) { + if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; + + const fuse_read_in* req = static_cast(data); + uint64_t offset = req->offset; + uint32_t size = req->size; + + // The docs on the fuse kernel interface are vague about what to do when a read request extends + // past the end of the file. We can return a short read -- the return structure does include a + // length field -- but in testing that caused the program using the file to segfault. (I + // speculate that this is due to the reading program accessing it via mmap; maybe mmap dislikes + // when you return something short of a whole page?) To fix this we zero-pad reads that extend + // past the end of the file so we're always returning exactly as many bytes as were requested. + // (Users of the mapped file have to know its real length anyway.) + + fuse_out_header outhdr; + outhdr.len = sizeof(outhdr) + size; + outhdr.error = 0; + outhdr.unique = hdr->unique; + + struct iovec vec[3]; + vec[0].iov_base = &outhdr; + vec[0].iov_len = sizeof(outhdr); + + uint32_t block = offset / fd->block_size; + int result = fetch_block(fd, block); + if (result != 0) return result; + + // Two cases: + // + // - the read request is entirely within this block. In this case we can reply immediately. + // + // - the read request goes over into the next block. Note that since we mount the filesystem + // with max_read=block_size, a read can never span more than two blocks. In this case we copy + // the block to extra_block and issue a fetch for the following block. + + uint32_t block_offset = offset - (block * fd->block_size); + + int vec_used; + if (size + block_offset <= fd->block_size) { + // First case: the read fits entirely in the first block. + + vec[1].iov_base = fd->block_data + block_offset; + vec[1].iov_len = size; + vec_used = 2; + } else { + // Second case: the read spills over into the next block. + + memcpy(fd->extra_block, fd->block_data + block_offset, fd->block_size - block_offset); + vec[1].iov_base = fd->extra_block; + vec[1].iov_len = fd->block_size - block_offset; + + result = fetch_block(fd, block + 1); if (result != 0) return result; + vec[2].iov_base = fd->block_data; + vec[2].iov_len = size - vec[1].iov_len; + vec_used = 3; + } - // Two cases: - // - // - the read request is entirely within this block. In this - // case we can reply immediately. - // - // - the read request goes over into the next block. Note that - // since we mount the filesystem with max_read=block_size, a - // read can never span more than two blocks. In this case we - // copy the block to extra_block and issue a fetch for the - // following block. - - uint32_t block_offset = offset - (block * fd->block_size); - - if (size + block_offset <= fd->block_size) { - // First case: the read fits entirely in the first block. - - vec[1].iov_base = fd->block_data + block_offset; - vec[1].iov_len = size; - vec_used = 2; - } else { - // Second case: the read spills over into the next block. - - memcpy(fd->extra_block, fd->block_data + block_offset, - fd->block_size - block_offset); - vec[1].iov_base = fd->extra_block; - vec[1].iov_len = fd->block_size - block_offset; - - result = fetch_block(fd, block+1); - if (result != 0) return result; - vec[2].iov_base = fd->block_data; - vec[2].iov_len = size - vec[1].iov_len; - vec_used = 3; - } - - if (writev(fd->ffd, vec, vec_used) < 0) { - printf("*** READ REPLY FAILED: %s ***\n", strerror(errno)); - } - return NO_STATUS; + if (writev(fd->ffd, vec, vec_used) == -1) { + printf("*** READ REPLY FAILED: %s ***\n", strerror(errno)); + } + return NO_STATUS; } -int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_size, - uint32_t block_size) { +int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size, + const char* mount_point) { // If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a // previous abnormal exit.) - umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE); + umount2(mount_point, MNT_FORCE); // fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read. if (block_size < 4096) { @@ -383,9 +356,8 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si return -1; } - struct fuse_data fd = {}; + fuse_data fd = {}; fd.vtab = vtab; - fd.cookie = cookie; fd.file_size = file_size; fd.block_size = block_size; fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1); @@ -397,33 +369,27 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si goto done; } - fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH); - if (fd.hashes == NULL) { - fprintf(stderr, "failed to allocate %d bites for hashes\n", - fd.file_blocks * SHA256_DIGEST_LENGTH); - result = -1; - goto done; - } - + // All hashes will be zero-initialized. + fd.hashes.resize(fd.file_blocks); fd.uid = getuid(); fd.gid = getgid(); fd.curr_block = -1; - fd.block_data = (uint8_t*)malloc(block_size); - if (fd.block_data == NULL) { + fd.block_data = static_cast(malloc(block_size)); + if (fd.block_data == nullptr) { fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size); result = -1; goto done; } - fd.extra_block = (uint8_t*)malloc(block_size); - if (fd.extra_block == NULL) { + fd.extra_block = static_cast(malloc(block_size)); + if (fd.extra_block == nullptr) { fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size); result = -1; goto done; } - fd.ffd = open("/dev/fuse", O_RDWR); - if (fd.ffd < 0) { + fd.ffd.reset(open("/dev/fuse", O_RDWR)); + if (!fd.ffd) { perror("open /dev/fuse"); result = -1; goto done; @@ -431,18 +397,18 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si { std::string opts = android::base::StringPrintf( - "fd=%d,user_id=%d,group_id=%d,max_read=%u,allow_other,rootmode=040000", fd.ffd, fd.uid, - fd.gid, block_size); + "fd=%d,user_id=%d,group_id=%d,max_read=%u,allow_other,rootmode=040000", fd.ffd.get(), + fd.uid, fd.gid, block_size); - result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, "fuse", - MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts.c_str()); - if (result < 0) { + result = mount("/dev/fuse", mount_point, "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, + opts.c_str()); + if (result == -1) { perror("mount"); goto done; } } - uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX * 8]; + uint8_t request_buffer[sizeof(fuse_in_header) + PATH_MAX * 8]; for (;;) { ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer))); if (len == -1) { @@ -454,13 +420,13 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si continue; } - if (static_cast(len) < sizeof(struct fuse_in_header)) { + if (static_cast(len) < sizeof(fuse_in_header)) { fprintf(stderr, "request too short: len=%zd\n", len); continue; } - struct fuse_in_header* hdr = reinterpret_cast(request_buffer); - void* data = request_buffer + sizeof(struct fuse_in_header); + fuse_in_header* hdr = reinterpret_cast(request_buffer); + void* data = request_buffer + sizeof(fuse_in_header); result = -ENOSYS; @@ -504,7 +470,7 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si } if (result != NO_STATUS) { - struct fuse_out_header outhdr; + fuse_out_header outhdr; outhdr.len = sizeof(outhdr); outhdr.error = result; outhdr.unique = hdr->unique; @@ -513,15 +479,12 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_si } done: - fd.vtab->close(fd.cookie); + fd.vtab.close(); - result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH); - if (result < 0) { - printf("fuse_sideload umount failed: %s\n", strerror(errno)); + if (umount2(mount_point, MNT_DETACH) == -1) { + fprintf(stderr, "fuse_sideload umount failed: %s\n", strerror(errno)); } - if (fd.ffd) close(fd.ffd); - free(fd.hashes); free(fd.block_data); free(fd.extra_block); diff --git a/fuse_sideload.h b/fuse_sideload.h index c0b16efbe..1b34cbdb0 100644 --- a/fuse_sideload.h +++ b/fuse_sideload.h @@ -17,22 +17,24 @@ #ifndef __FUSE_SIDELOAD_H #define __FUSE_SIDELOAD_H -// define the filenames created by the sideload FUSE filesystem -#define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload" -#define FUSE_SIDELOAD_HOST_FILENAME "package.zip" -#define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME) -#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit" -#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG) +#include + +// Define the filenames created by the sideload FUSE filesystem. +static constexpr const char* FUSE_SIDELOAD_HOST_MOUNTPOINT = "/sideload"; +static constexpr const char* FUSE_SIDELOAD_HOST_FILENAME = "package.zip"; +static constexpr const char* FUSE_SIDELOAD_HOST_PATHNAME = "/sideload/package.zip"; +static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_FLAG = "exit"; +static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_PATHNAME = "/sideload/exit"; struct provider_vtab { - // read a block - int (*read_block)(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size); + // read a block + std::function read_block; - // close down - void (*close)(void* cookie); + // close down + std::function close; }; -int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, - uint64_t file_size, uint32_t block_size); +int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size, + const char* mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT); #endif diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp index 0f4c2563d..9bd3f2392 100644 --- a/minadbd/fuse_adb_provider.cpp +++ b/minadbd/fuse_adb_provider.cpp @@ -14,46 +14,43 @@ * limitations under the License. */ -#include +#include "fuse_adb_provider.h" + +#include #include +#include #include -#include + +#include #include "adb.h" #include "adb_io.h" -#include "fuse_adb_provider.h" #include "fuse_sideload.h" -int read_block_adb(void* data, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { - adb_data* ad = reinterpret_cast(data); - - if (!WriteFdFmt(ad->sfd, "%08u", block)) { - fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno)); - return -EIO; - } +int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { + if (!WriteFdFmt(ad.sfd, "%08u", block)) { + fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno)); + return -EIO; + } - if (!ReadFdExactly(ad->sfd, buffer, fetch_size)) { - fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno)); - return -EIO; - } - - return 0; -} + if (!ReadFdExactly(ad.sfd, buffer, fetch_size)) { + fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno)); + return -EIO; + } -static void close_adb(void* data) { - adb_data* ad = reinterpret_cast(data); - WriteFdExactly(ad->sfd, "DONEDONE"); + return 0; } int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) { - adb_data ad; - ad.sfd = sfd; - ad.file_size = file_size; - ad.block_size = block_size; + adb_data ad; + ad.sfd = sfd; + ad.file_size = file_size; + ad.block_size = block_size; - provider_vtab vtab; - vtab.read_block = read_block_adb; - vtab.close = close_adb; + provider_vtab vtab; + vtab.read_block = std::bind(read_block_adb, ad, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3); + vtab.close = [&ad]() { WriteFdExactly(ad.sfd, "DONEDONE"); }; - return run_fuse_sideload(&vtab, &ad, file_size, block_size); + return run_fuse_sideload(vtab, file_size, block_size); } diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h index 9941709b9..36d86d539 100644 --- a/minadbd/fuse_adb_provider.h +++ b/minadbd/fuse_adb_provider.h @@ -20,13 +20,13 @@ #include struct adb_data { - int sfd; // file descriptor for the adb channel + int sfd; // file descriptor for the adb channel - uint64_t file_size; - uint32_t block_size; + uint64_t file_size; + uint32_t block_size; }; -int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size); +int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size); int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size); #endif diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp index 31be2a64e..00250e505 100644 --- a/minadbd/fuse_adb_provider_test.cpp +++ b/minadbd/fuse_adb_provider_test.cpp @@ -46,8 +46,8 @@ TEST(fuse_adb_provider, read_block_adb) { uint32_t block = 1234U; const char expected_block[] = "00001234"; - ASSERT_EQ(0, read_block_adb(static_cast(&data), block, - reinterpret_cast(block_data), sizeof(expected_data) - 1)); + ASSERT_EQ(0, read_block_adb(data, block, reinterpret_cast(block_data), + sizeof(expected_data) - 1)); // Check that read_block_adb requested the right block. char block_req[sizeof(expected_block)] = {}; @@ -84,7 +84,7 @@ TEST(fuse_adb_provider, read_block_adb_fail_write) { signal(SIGPIPE, SIG_IGN); char buf[1]; - ASSERT_EQ(-EIO, read_block_adb(static_cast(&data), 0, reinterpret_cast(buf), 1)); + ASSERT_EQ(-EIO, read_block_adb(data, 0, reinterpret_cast(buf), 1)); close(sockets[0]); } diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp index 40cfc6975..b7109fcc2 100644 --- a/tests/component/sideload_test.cpp +++ b/tests/component/sideload_test.cpp @@ -16,6 +16,12 @@ #include +#include +#include + +#include +#include +#include #include #include "fuse_sideload.h" @@ -26,11 +32,67 @@ TEST(SideloadTest, fuse_device) { TEST(SideloadTest, run_fuse_sideload_wrong_parameters) { provider_vtab vtab; - vtab.close = [](void*) {}; + vtab.close = [](void) {}; - ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, 4095)); - ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, (1 << 22) + 1)); + ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, 4095)); + ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, (1 << 22) + 1)); // Too many blocks. - ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, ((1 << 18) + 1) * 4096, 4096)); + ASSERT_EQ(-1, run_fuse_sideload(vtab, ((1 << 18) + 1) * 4096, 4096)); +} + +TEST(SideloadTest, run_fuse_sideload) { + const std::vector blocks = { + std::string(2048, 'a') + std::string(2048, 'b'), + std::string(2048, 'c') + std::string(2048, 'd'), + std::string(2048, 'e') + std::string(2048, 'f'), + std::string(2048, 'g') + std::string(2048, 'h'), + }; + const std::string content = android::base::Join(blocks, ""); + ASSERT_EQ(16384U, content.size()); + + provider_vtab vtab; + vtab.close = [](void) {}; + vtab.read_block = [&blocks](uint32_t block, uint8_t* buffer, uint32_t fetch_size) { + if (block >= 4) return -1; + blocks[block].copy(reinterpret_cast(buffer), fetch_size); + return 0; + }; + + TemporaryDir mount_point; + pid_t pid = fork(); + if (pid == 0) { + ASSERT_EQ(0, run_fuse_sideload(vtab, 16384, 4096, mount_point.path)); + _exit(EXIT_SUCCESS); + } + + std::string package = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME; + int status; + static constexpr int kSideloadInstallTimeout = 10; + for (int i = 0; i < kSideloadInstallTimeout; ++i) { + ASSERT_NE(-1, waitpid(pid, &status, WNOHANG)); + + struct stat sb; + if (stat(package.c_str(), &sb) == 0) { + break; + } + + if (errno == ENOENT && i < kSideloadInstallTimeout - 1) { + sleep(1); + continue; + } + FAIL() << "Timed out waiting for the fuse-provided package."; + } + + std::string content_via_fuse; + ASSERT_TRUE(android::base::ReadFileToString(package, &content_via_fuse)); + ASSERT_EQ(content, content_via_fuse); + + std::string exit_flag = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG; + struct stat sb; + ASSERT_EQ(0, stat(exit_flag.c_str(), &sb)); + + waitpid(pid, &status, 0); + ASSERT_EQ(0, WEXITSTATUS(status)); + ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); } -- cgit v1.2.3 From 6798315327690fdbe93add15159be5b925779bfe Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sat, 4 Nov 2017 00:08:08 -0700 Subject: otautil: Remove the aborts in RangeSet::Parse(). We used to CHECK and abort on parsing errors. While it works fine for the updater use case (because recovery starts updater in a forked process and collects the process exit code), it's difficult for other clients to use RangeSet as a library (e.g. update_verifier). This CL switches the aborts to returning empty RangeSet instead. Callers need to check the parsing results explicitly. The CL also separates RangeSet::PushBack() into a function, and moves SortedRangeSet::Clear() into RangeSet. Test: recovery_unit_test Test: Sideload an OTA package with the new updater on angler. Test: Sideload an OTA package with injected range string errors. The updater aborts from the explicit checks. Change-Id: If2b7f6f41dc93af917a21c7877a83e98dc3fd016 --- otautil/include/otautil/rangeset.h | 42 ++++++++++++++----- otautil/rangeset.cpp | 85 +++++++++++++++++++++++++------------- tests/unit/rangeset_test.cpp | 78 +++++++++++++++++++++++++++------- updater/blockimg.cpp | 20 ++++++++- 4 files changed, 170 insertions(+), 55 deletions(-) diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h index c4234d181..af8ae2dee 100644 --- a/otautil/include/otautil/rangeset.h +++ b/otautil/include/otautil/rangeset.h @@ -30,28 +30,35 @@ class RangeSet { explicit RangeSet(std::vector&& pairs); + // Parses the given string into a RangeSet. Returns the parsed RangeSet, or an empty RangeSet on + // errors. static RangeSet Parse(const std::string& range_text); + // Appends the given Range to the current RangeSet. + bool PushBack(Range range); + + // Clears all the ranges from the RangeSet. + void Clear(); + std::string ToString() const; - // Get the block number for the i-th (starting from 0) block in the RangeSet. + // Gets the block number for the i-th (starting from 0) block in the RangeSet. size_t GetBlockNumber(size_t idx) const; - // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" - // and "5,7" are not overlapped. + // Returns whether the current RangeSet overlaps with other. RangeSet has half-closed half-open + // bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" and "5,7" are not overlapped. bool Overlaps(const RangeSet& other) const; - // size() gives the number of Range's in this RangeSet. + // Returns the number of Range's in this RangeSet. size_t size() const { return ranges_.size(); } - // blocks() gives the number of all blocks in this RangeSet. + // Returns the total number of blocks in this RangeSet. size_t blocks() const { return blocks_; } - // We provide const iterators only. std::vector::const_iterator cbegin() const { return ranges_.cbegin(); } @@ -60,13 +67,20 @@ class RangeSet { return ranges_.cend(); } - // Need to provide begin()/end() since range-based loop expects begin()/end(). + std::vector::iterator begin() { + return ranges_.begin(); + } + + std::vector::iterator end() { + return ranges_.end(); + } + std::vector::const_iterator begin() const { - return ranges_.cbegin(); + return ranges_.begin(); } std::vector::const_iterator end() const { - return ranges_.cend(); + return ranges_.end(); } // Reverse const iterators for MoveRange(). @@ -78,6 +92,11 @@ class RangeSet { return ranges_.crend(); } + // Returns whether the RangeSet is valid (i.e. non-empty). + explicit operator bool() const { + return !ranges_.empty(); + } + const Range& operator[](size_t i) const { return ranges_[i]; } @@ -109,6 +128,9 @@ class RangeSet { // every block in the original source. class SortedRangeSet : public RangeSet { public: + // The block size when working with offset and file length. + static constexpr size_t kBlockSize = 4096; + SortedRangeSet() {} // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. @@ -122,8 +144,6 @@ class SortedRangeSet : public RangeSet { // Compute the block range the file occupies, and insert that range. void Insert(size_t start, size_t len); - void Clear(); - using RangeSet::Overlaps; bool Overlaps(size_t start, size_t len) const; diff --git a/otautil/rangeset.cpp b/otautil/rangeset.cpp index a121a4efc..532cba4a8 100644 --- a/otautil/rangeset.cpp +++ b/otautil/rangeset.cpp @@ -16,8 +16,10 @@ #include "otautil/rangeset.h" +#include #include +#include #include #include #include @@ -28,47 +30,79 @@ #include RangeSet::RangeSet(std::vector&& pairs) { - CHECK_NE(pairs.size(), static_cast(0)) << "Invalid number of tokens"; + blocks_ = 0; + if (pairs.empty()) { + LOG(ERROR) << "Invalid number of tokens"; + return; + } - // Sanity check the input. - size_t result = 0; for (const auto& range : pairs) { - CHECK_LT(range.first, range.second) << "Empty or negative range: " << range.first << ", " - << range.second; - size_t sz = range.second - range.first; - CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow"; - result += sz; + if (!PushBack(range)) { + Clear(); + return; + } } - - ranges_ = pairs; - blocks_ = result; } RangeSet RangeSet::Parse(const std::string& range_text) { std::vector pieces = android::base::Split(range_text, ","); - CHECK_GE(pieces.size(), static_cast(3)) << "Invalid range text: " << range_text; + if (pieces.size() < 3) { + LOG(ERROR) << "Invalid range text: " << range_text; + return {}; + } size_t num; - CHECK(android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) - << "Failed to parse the number of tokens: " << range_text; - - CHECK_NE(num, static_cast(0)) << "Invalid number of tokens: " << range_text; - CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; - CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; + if (!android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) { + LOG(ERROR) << "Failed to parse the number of tokens: " << range_text; + return {}; + } + if (num == 0) { + LOG(ERROR) << "Invalid number of tokens: " << range_text; + return {}; + } + if (num % 2 != 0) { + LOG(ERROR) << "Number of tokens must be even: " << range_text; + return {}; + } + if (num != pieces.size() - 1) { + LOG(ERROR) << "Mismatching number of tokens: " << range_text; + return {}; + } std::vector pairs; for (size_t i = 0; i < num; i += 2) { size_t first; - CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX))); size_t second; - CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))); - + if (!android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX)) || + !android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))) { + return {}; + } pairs.emplace_back(first, second); } - return RangeSet(std::move(pairs)); } +bool RangeSet::PushBack(Range range) { + if (range.first >= range.second) { + LOG(ERROR) << "Empty or negative range: " << range.first << ", " << range.second; + return false; + } + size_t sz = range.second - range.first; + if (blocks_ >= SIZE_MAX - sz) { + LOG(ERROR) << "RangeSet size overflow"; + return false; + } + + ranges_.push_back(std::move(range)); + blocks_ += sz; + return true; +} + +void RangeSet::Clear() { + ranges_.clear(); + blocks_ = 0; +} + std::string RangeSet::ToString() const { if (ranges_.empty()) { return ""; @@ -114,8 +148,6 @@ bool RangeSet::Overlaps(const RangeSet& other) const { return false; } -static constexpr size_t kBlockSize = 4096; - // Ranges in the the set should be mutually exclusive; and they're sorted by the start block. SortedRangeSet::SortedRangeSet(std::vector&& pairs) : RangeSet(std::move(pairs)) { std::sort(ranges_.begin(), ranges_.end()); @@ -158,11 +190,6 @@ void SortedRangeSet::Insert(size_t start, size_t len) { Insert(to_insert); } -void SortedRangeSet::Clear() { - blocks_ = 0; - ranges_.clear(); -} - bool SortedRangeSet::Overlaps(size_t start, size_t len) const { RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } }); return Overlaps(rs); diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp index b3ed99215..5141bb67f 100644 --- a/tests/unit/rangeset_test.cpp +++ b/tests/unit/rangeset_test.cpp @@ -17,12 +17,24 @@ #include #include +#include #include #include #include "otautil/rangeset.h" +TEST(RangeSetTest, ctor) { + RangeSet rs(std::vector{ Range{ 8, 10 }, Range{ 1, 5 } }); + ASSERT_TRUE(rs); + + RangeSet rs2(std::vector{}); + ASSERT_FALSE(rs2); + + RangeSet rs3(std::vector{ Range{ 8, 10 }, Range{ 5, 1 } }); + ASSERT_FALSE(rs3); +} + TEST(RangeSetTest, Parse_smoke) { RangeSet rs = RangeSet::Parse("2,1,10"); ASSERT_EQ(static_cast(1), rs.size()); @@ -37,27 +49,64 @@ TEST(RangeSetTest, Parse_smoke) { // Leading zeros are fine. But android::base::ParseUint() doesn't like trailing zeros like "10 ". ASSERT_EQ(rs, RangeSet::Parse(" 2, 1, 10")); - ASSERT_EXIT(RangeSet::Parse("2,1,10 "), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_FALSE(RangeSet::Parse("2,1,10 ")); } TEST(RangeSetTest, Parse_InvalidCases) { // Insufficient number of tokens. - ASSERT_EXIT(RangeSet::Parse(""), ::testing::KilledBySignal(SIGABRT), ""); - ASSERT_EXIT(RangeSet::Parse("2,1"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_FALSE(RangeSet::Parse("")); + ASSERT_FALSE(RangeSet::Parse("2,1")); // The first token (i.e. the number of following tokens) is invalid. - ASSERT_EXIT(RangeSet::Parse("a,1,1"), ::testing::KilledBySignal(SIGABRT), ""); - ASSERT_EXIT(RangeSet::Parse("3,1,1"), ::testing::KilledBySignal(SIGABRT), ""); - ASSERT_EXIT(RangeSet::Parse("-3,1,1"), ::testing::KilledBySignal(SIGABRT), ""); - ASSERT_EXIT(RangeSet::Parse("2,1,2,3"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_FALSE(RangeSet::Parse("a,1,1")); + ASSERT_FALSE(RangeSet::Parse("3,1,1")); + ASSERT_FALSE(RangeSet::Parse("-3,1,1")); + ASSERT_FALSE(RangeSet::Parse("2,1,2,3")); // Invalid tokens. - ASSERT_EXIT(RangeSet::Parse("2,1,10a"), ::testing::KilledBySignal(SIGABRT), ""); - ASSERT_EXIT(RangeSet::Parse("2,,10"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_FALSE(RangeSet::Parse("2,1,10a")); + ASSERT_FALSE(RangeSet::Parse("2,,10")); // Empty or negative range. - ASSERT_EXIT(RangeSet::Parse("2,2,2"), ::testing::KilledBySignal(SIGABRT), ""); - ASSERT_EXIT(RangeSet::Parse("2,2,1"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_FALSE(RangeSet::Parse("2,2,2")); + ASSERT_FALSE(RangeSet::Parse("2,2,1")); +} + +TEST(RangeSetTest, Clear) { + RangeSet rs = RangeSet::Parse("2,1,6"); + ASSERT_TRUE(rs); + rs.Clear(); + ASSERT_FALSE(rs); + + // No-op to clear an empty RangeSet. + rs.Clear(); + ASSERT_FALSE(rs); +} + +TEST(RangeSetTest, PushBack) { + RangeSet rs; + ASSERT_FALSE(rs); + + ASSERT_TRUE(rs.PushBack({ 3, 5 })); + ASSERT_EQ(RangeSet::Parse("2,3,5"), rs); + + ASSERT_TRUE(rs.PushBack({ 5, 15 })); + ASSERT_EQ(RangeSet::Parse("4,3,5,5,15"), rs); + ASSERT_EQ(static_cast(2), rs.size()); + ASSERT_EQ(static_cast(12), rs.blocks()); +} + +TEST(RangeSetTest, PushBack_InvalidInput) { + RangeSet rs; + ASSERT_FALSE(rs); + ASSERT_FALSE(rs.PushBack({ 5, 3 })); + ASSERT_FALSE(rs); + ASSERT_FALSE(rs.PushBack({ 15, 15 })); + ASSERT_FALSE(rs); + + ASSERT_TRUE(rs.PushBack({ 5, 15 })); + ASSERT_FALSE(rs.PushBack({ 5, std::numeric_limits::max() - 2 })); + ASSERT_EQ(RangeSet::Parse("2,5,15"), rs); } TEST(RangeSetTest, Overlaps) { @@ -90,7 +139,7 @@ TEST(RangeSetTest, equality) { ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,7")); ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,2,7")); - // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5". + // The orders of Range's matter, e.g. "4,1,5,8,10" != "4,8,10,1,5". ASSERT_NE(RangeSet::Parse("4,1,5,8,10"), RangeSet::Parse("4,8,10,1,5")); } @@ -111,13 +160,14 @@ TEST(RangeSetTest, iterators) { ASSERT_EQ((std::vector{ Range{ 8, 10 }, Range{ 1, 5 } }), ranges); } -TEST(RangeSetTest, tostring) { +TEST(RangeSetTest, ToString) { + ASSERT_EQ("", RangeSet::Parse("").ToString()); ASSERT_EQ("2,1,6", RangeSet::Parse("2,1,6").ToString()); ASSERT_EQ("4,1,5,8,10", RangeSet::Parse("4,1,5,8,10").ToString()); ASSERT_EQ("6,1,3,4,6,15,22", RangeSet::Parse("6,1,3,4,6,15,22").ToString()); } -TEST(SortedRangeSetTest, insertion) { +TEST(SortedRangeSetTest, Insert) { SortedRangeSet rs({ { 2, 3 }, { 4, 6 }, { 8, 14 } }); rs.Insert({ 1, 2 }); ASSERT_EQ(SortedRangeSet({ { 1, 3 }, { 4, 6 }, { 8, 14 } }), rs); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 6c7b3efcf..1c931afef 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -492,6 +492,10 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params, } RangeSet src = RangeSet::Parse(params.tokens[pos++]); + if (!src) { + LOG(ERROR) << "Failed to parse range in " << params.cmdline; + return; + } RangeSet locs; // If there's no stashed blocks, content in the buffer is consecutive and has the same @@ -936,6 +940,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size params.cpos++; } else { RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]); + CHECK(static_cast(src)); *overlap = src.Overlaps(tgt); if (ReadBlocks(src, params.buffer, params.fd) == -1) { @@ -948,6 +953,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size } RangeSet locs = RangeSet::Parse(params.tokens[params.cpos++]); + CHECK(static_cast(locs)); MoveRange(params.buffer, locs, params.buffer); } @@ -970,6 +976,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size } RangeSet locs = RangeSet::Parse(tokens[1]); + CHECK(static_cast(locs)); MoveRange(params.buffer, locs, stash); } @@ -1034,6 +1041,7 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* // tgt = RangeSet::Parse(params.tokens[params.cpos++]); + CHECK(static_cast(tgt)); std::vector tgtbuffer(tgt.blocks() * BLOCKSIZE); if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { @@ -1146,6 +1154,7 @@ static int PerformCommandStash(CommandParameters& params) { } RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]); + CHECK(static_cast(src)); allocate(src.blocks() * BLOCKSIZE, params.buffer); if (ReadBlocks(src, params.buffer, params.fd) == -1) { @@ -1196,6 +1205,7 @@ static int PerformCommandZero(CommandParameters& params) { } RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); + CHECK(static_cast(tgt)); LOG(INFO) << " zeroing " << tgt.blocks() << " blocks"; @@ -1238,6 +1248,7 @@ static int PerformCommandNew(CommandParameters& params) { } RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); + CHECK(static_cast(tgt)); if (params.canwrite) { LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data"; @@ -1368,6 +1379,7 @@ static int PerformCommandErase(CommandParameters& params) { } RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); + CHECK(static_cast(tgt)); if (params.canwrite) { LOG(INFO) << " erasing " << tgt.blocks() << " blocks"; @@ -1773,6 +1785,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vectordata); + CHECK(static_cast(rs)); SHA_CTX ctx; SHA1_Init(&ctx); @@ -1884,6 +1897,11 @@ Value* BlockImageRecoverFn(const char* name, State* state, ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); return StringValue(""); } + RangeSet rs = RangeSet::Parse(ranges->data); + if (!rs) { + ErrorAbort(state, kArgsParsingFailure, "failed to parse ranges: %s", ranges->data.c_str()); + return StringValue(""); + } // Output notice to log when recover is attempted LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; @@ -1909,7 +1927,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, } uint8_t buffer[BLOCKSIZE]; - for (const auto& range : RangeSet::Parse(ranges->data)) { + for (const auto& range : rs) { for (size_t j = range.first; j < range.second; ++j) { // Stay within the data area, libfec validates and corrects metadata if (status.data_size <= static_cast(j) * BLOCKSIZE) { -- cgit v1.2.3 From 3afe5f56916302784b428d027ad138359d832dbd Mon Sep 17 00:00:00 2001 From: Alex Deymo Date: Wed, 8 Nov 2017 12:26:44 +0100 Subject: Include bspatch.h from bsdiff/ The function ApplyBSDiffPatch() defined in bspatch.cpp is declared in applypatch.h, but it includes "bspatch.h" from the bsdiff/ project, which is at least confusing. There is no "bspatch.h" in this repo, so the include actually reffers to the one in bsdiff. This patch uses the "bsdiff/bspatch.h" form instead to avoid confusion. Bug: None Test: It builds. Change-Id: I6b6ffd6725b2b34ff644aed93683f69779103661 --- applypatch/bspatch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index c291464a8..db2ce0896 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include "applypatch/applypatch.h" -- cgit v1.2.3 From 160514bf2bac2ad8b1af6cb5a72d88439596ada1 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sat, 4 Nov 2017 00:08:08 -0700 Subject: Load-balancing update_verifier worker threads. Prior to this CL, the block verification works were assigned based on the pattern of the ranges, which could lead to unbalanced workloads. This CL adds RangeSet::Split() and moves update_verifier over. a) For the following care_map.txt on walleye: system 20,0,347,348,540,556,32770,33084,98306,98620,163842,164156,229378,229692,294914,295228,524289,524291,524292,524348,529059 vendor 8,0,120,135,32770,32831,94564,98304,98306 Measured the time costs prior to and with this CL with the following script. $ cat test_update_verifier.sh #!/bin/sh adb shell stop adb shell "cp /data/local/tmp/care_map.txt /data/ota_package/" for i in $(seq 1 50) do echo "Iteration: $i" adb shell "bootctl set-active-boot-slot 0" adb shell "echo 3 > /proc/sys/vm/drop_caches" adb shell "time /data/local/tmp/update_verifier" sleep 3 done Without this CL, the average time cost is 5.66s, while with the CL it's reduced to 3.2s. b) For the following care_map.txt, measured the performance on marlin: system 18,0,271,286,457,8350,32770,33022,98306,98558,163842,164094,196609,204800,229378,229630,294914,295166,501547 vendor 10,0,42,44,85,2408,32770,32806,32807,36902,74242 It takes 12.9s and 5.6s without and with the CL respectively. Fixes: 68553827 Test: recovery_unit_test Test: Flash new build and trigger update_verifier. Check the balanced block verification. Change-Id: I5fa4bf09a84e6b9b0975ee5f522724464181333f --- otautil/include/otautil/rangeset.h | 8 ++++ otautil/rangeset.cpp | 40 +++++++++++++++++++ tests/unit/rangeset_test.cpp | 80 +++++++++++++++++++++++++++++++++++++ update_verifier/Android.mk | 8 +++- update_verifier/update_verifier.cpp | 45 +++++++++------------ 5 files changed, 154 insertions(+), 27 deletions(-) diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h index af8ae2dee..e91d02ca6 100644 --- a/otautil/include/otautil/rangeset.h +++ b/otautil/include/otautil/rangeset.h @@ -49,6 +49,14 @@ class RangeSet { // bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" and "5,7" are not overlapped. bool Overlaps(const RangeSet& other) const; + // Returns a vector of RangeSets that contain the same set of blocks represented by the current + // RangeSet. The RangeSets in the vector contain similar number of blocks, with a maximum delta + // of 1-block between any two of them. For example, 14 blocks would be split into 4 + 4 + 3 + 3, + // as opposed to 4 + 4 + 4 + 2. If the total number of blocks (T) is less than groups, it + // returns a vector of T 1-block RangeSets. Otherwise the number of the returned RangeSets must + // equal to groups. The current RangeSet remains intact after the split. + std::vector Split(size_t groups) const; + // Returns the number of Range's in this RangeSet. size_t size() const { return ranges_.size(); diff --git a/otautil/rangeset.cpp b/otautil/rangeset.cpp index 532cba4a8..96955b9d0 100644 --- a/otautil/rangeset.cpp +++ b/otautil/rangeset.cpp @@ -103,6 +103,46 @@ void RangeSet::Clear() { blocks_ = 0; } +std::vector RangeSet::Split(size_t groups) const { + if (ranges_.empty() || groups == 0) return {}; + + if (blocks_ < groups) { + groups = blocks_; + } + + // Evenly distribute blocks, with the first few groups possibly containing one more. + size_t mean = blocks_ / groups; + std::vector blocks_per_group(groups, mean); + std::fill_n(blocks_per_group.begin(), blocks_ % groups, mean + 1); + + std::vector result; + + // Forward iterate Ranges and fill up each group with the desired number of blocks. + auto it = ranges_.cbegin(); + Range range = *it; + for (const auto& blocks : blocks_per_group) { + RangeSet buffer; + size_t needed = blocks; + while (needed > 0) { + size_t range_blocks = range.second - range.first; + if (range_blocks > needed) { + // Split the current range and don't advance the iterator. + buffer.PushBack({ range.first, range.first + needed }); + range.first = range.first + needed; + break; + } + buffer.PushBack(range); + it++; + if (it != ranges_.cend()) { + range = *it; + } + needed -= range_blocks; + } + result.push_back(std::move(buffer)); + } + return result; +} + std::string RangeSet::ToString() const { if (ranges_.empty()) { return ""; diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp index 5141bb67f..7ae193e18 100644 --- a/tests/unit/rangeset_test.cpp +++ b/tests/unit/rangeset_test.cpp @@ -123,6 +123,86 @@ TEST(RangeSetTest, Overlaps) { ASSERT_FALSE(RangeSet::Parse("2,5,7").Overlaps(RangeSet::Parse("2,3,5"))); } +TEST(RangeSetTest, Split) { + RangeSet rs1 = RangeSet::Parse("2,1,2"); + ASSERT_TRUE(rs1); + ASSERT_EQ((std::vector{ RangeSet::Parse("2,1,2") }), rs1.Split(1)); + + RangeSet rs2 = RangeSet::Parse("2,5,10"); + ASSERT_TRUE(rs2); + ASSERT_EQ((std::vector{ RangeSet::Parse("2,5,8"), RangeSet::Parse("2,8,10") }), + rs2.Split(2)); + + RangeSet rs3 = RangeSet::Parse("4,0,1,5,10"); + ASSERT_TRUE(rs3); + ASSERT_EQ((std::vector{ RangeSet::Parse("4,0,1,5,7"), RangeSet::Parse("2,7,10") }), + rs3.Split(2)); + + RangeSet rs4 = RangeSet::Parse("6,1,3,3,4,4,5"); + ASSERT_TRUE(rs4); + ASSERT_EQ((std::vector{ RangeSet::Parse("2,1,3"), RangeSet::Parse("2,3,4"), + RangeSet::Parse("2,4,5") }), + rs4.Split(3)); + + RangeSet rs5 = RangeSet::Parse("2,0,10"); + ASSERT_TRUE(rs5); + ASSERT_EQ((std::vector{ RangeSet::Parse("2,0,3"), RangeSet::Parse("2,3,6"), + RangeSet::Parse("2,6,8"), RangeSet::Parse("2,8,10") }), + rs5.Split(4)); + + RangeSet rs6 = RangeSet::Parse( + "20,0,268,269,271,286,447,8350,32770,33022,98306,98558,163842,164094,196609,204800,229378," + "229630,294914,295166,457564"); + ASSERT_TRUE(rs6); + size_t rs6_blocks = rs6.blocks(); + auto splits = rs6.Split(4); + ASSERT_EQ( + (std::vector{ + RangeSet::Parse("12,0,268,269,271,286,447,8350,32770,33022,98306,98558,118472"), + RangeSet::Parse("8,118472,163842,164094,196609,204800,229378,229630,237216"), + RangeSet::Parse("4,237216,294914,295166,347516"), RangeSet::Parse("2,347516,457564") }), + splits); + size_t sum = 0; + for (const auto& element : splits) { + sum += element.blocks(); + } + ASSERT_EQ(rs6_blocks, sum); +} + +TEST(RangeSetTest, Split_EdgeCases) { + // Empty RangeSet. + RangeSet rs1; + ASSERT_FALSE(rs1); + ASSERT_EQ((std::vector{}), rs1.Split(2)); + ASSERT_FALSE(rs1); + + // Zero group. + RangeSet rs2 = RangeSet::Parse("2,1,5"); + ASSERT_TRUE(rs2); + ASSERT_EQ((std::vector{}), rs2.Split(0)); + + // The number of blocks equals to the number of groups. + RangeSet rs3 = RangeSet::Parse("2,1,5"); + ASSERT_TRUE(rs3); + ASSERT_EQ((std::vector{ RangeSet::Parse("2,1,2"), RangeSet::Parse("2,2,3"), + RangeSet::Parse("2,3,4"), RangeSet::Parse("2,4,5") }), + rs3.Split(4)); + + // Less blocks than the number of groups. + RangeSet rs4 = RangeSet::Parse("2,1,5"); + ASSERT_TRUE(rs4); + ASSERT_EQ((std::vector{ RangeSet::Parse("2,1,2"), RangeSet::Parse("2,2,3"), + RangeSet::Parse("2,3,4"), RangeSet::Parse("2,4,5") }), + rs4.Split(8)); + + // Less blocks than the number of groups. + RangeSet rs5 = RangeSet::Parse("2,0,3"); + ASSERT_TRUE(rs5); + ASSERT_EQ((std::vector{ RangeSet::Parse("2,0,1"), RangeSet::Parse("2,1,2"), + RangeSet::Parse("2,2,3") }), + rs5.Split(4)); +} + TEST(RangeSetTest, GetBlockNumber) { RangeSet rs = RangeSet::Parse("2,1,10"); ASSERT_EQ(static_cast(1), rs.GetBlockNumber(0)); diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk index 33c5fe9e7..0ff88546f 100644 --- a/update_verifier/Android.mk +++ b/update_verifier/Android.mk @@ -22,6 +22,10 @@ LOCAL_SRC_FILES := \ update_verifier.cpp LOCAL_MODULE := libupdate_verifier + +LOCAL_STATIC_LIBRARIES := \ + libotautil + LOCAL_SHARED_LIBRARIES := \ libbase \ libcutils \ @@ -54,7 +58,9 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := update_verifier LOCAL_STATIC_LIBRARIES := \ - libupdate_verifier + libupdate_verifier \ + libotautil + LOCAL_SHARED_LIBRARIES := \ libbase \ libcutils \ diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index ba7b7aec4..c5e154f03 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -58,6 +58,8 @@ #include #include +#include "otautil/rangeset.h" + using android::sp; using android::hardware::boot::V1_0::IBootControl; using android::hardware::boot::V1_0::BoolResult; @@ -129,42 +131,33 @@ static bool read_blocks(const std::string& partition, const std::string& range_s // 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). - std::vector ranges = android::base::Split(range_str, ","); - size_t range_count; - bool status = android::base::ParseUint(ranges[0], &range_count); - if (!status || (range_count == 0) || (range_count % 2 != 0) || - (range_count != ranges.size() - 1)) { - LOG(ERROR) << "Error in parsing range string."; + RangeSet ranges = RangeSet::Parse(range_str); + if (!ranges) { + LOG(ERROR) << "Error parsing RangeSet string " << range_str; return false; } - range_count /= 2; - std::vector> threads; + // RangeSet::Split() splits the ranges into multiple groups with same number of blocks (except for + // the last group). size_t thread_num = std::thread::hardware_concurrency() ?: 4; - thread_num = std::min(thread_num, range_count); - size_t group_range_count = (range_count + thread_num - 1) / thread_num; + std::vector groups = ranges.Split(thread_num); - for (size_t t = 0; t < thread_num; t++) { - auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() { - size_t blk_count = 0; - static constexpr size_t kBlockSize = 4096; - std::vector buf(1024 * kBlockSize); + std::vector> threads; + for (const auto& group : groups) { + auto thread_func = [&group, &dm_block_device, &partition]() { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY))); if (fd.get() == -1) { PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition; return false; } - for (size_t i = group_range_count * 2 * t + 1; - i < std::min(group_range_count * 2 * (t + 1) + 1, ranges.size()); i += 2) { - unsigned int range_start, range_end; - bool parse_status = android::base::ParseUint(ranges[i], &range_start); - parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); - if (!parse_status || range_start >= range_end) { - LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1]; - return false; - } + static constexpr size_t kBlockSize = 4096; + std::vector buf(1024 * kBlockSize); + size_t block_count = 0; + for (const auto& range : group) { + size_t range_start = range.first; + size_t range_end = range.second; if (lseek64(fd.get(), static_cast(range_start) * kBlockSize, SEEK_SET) == -1) { PLOG(ERROR) << "lseek to " << range_start << " failed"; return false; @@ -179,9 +172,9 @@ static bool read_blocks(const std::string& partition, const std::string& range_s } remain -= to_read; } - blk_count += (range_end - range_start); + block_count += (range_end - range_start); } - LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device; + LOG(INFO) << "Finished reading " << block_count << " blocks on " << dm_block_device; return true; }; -- cgit v1.2.3 From a2a68a522e7601233a01787d01f1591e9efaa550 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 29 Sep 2017 14:06:20 -0700 Subject: uncrypt: Move to Soong. Test: mmma -j bootable/recovery Change-Id: I405f2a70f51904c02c49a287c23cbc115a4c5132 --- Android.bp | 1 + Android.mk | 1 - uncrypt/Android.bp | 39 +++++++++++++++++++++++++++++++++++++++ uncrypt/Android.mk | 31 ------------------------------- 4 files changed, 40 insertions(+), 32 deletions(-) create mode 100644 uncrypt/Android.bp delete mode 100644 uncrypt/Android.mk diff --git a/Android.bp b/Android.bp index 22407e0e2..f8c6a4b71 100644 --- a/Android.bp +++ b/Android.bp @@ -4,4 +4,5 @@ subdirs = [ "edify", "otafault", "otautil", + "uncrypt", ] diff --git a/Android.mk b/Android.mk index 985131b02..d9966b7cc 100644 --- a/Android.mk +++ b/Android.mk @@ -263,6 +263,5 @@ include \ $(LOCAL_PATH)/minui/Android.mk \ $(LOCAL_PATH)/tests/Android.mk \ $(LOCAL_PATH)/tools/Android.mk \ - $(LOCAL_PATH)/uncrypt/Android.mk \ $(LOCAL_PATH)/updater/Android.mk \ $(LOCAL_PATH)/update_verifier/Android.mk \ diff --git a/uncrypt/Android.bp b/uncrypt/Android.bp new file mode 100644 index 000000000..aa56d2f74 --- /dev/null +++ b/uncrypt/Android.bp @@ -0,0 +1,39 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_binary { + name: "uncrypt", + + srcs: [ + "uncrypt.cpp", + ], + + cflags: [ + "-Wall", + "-Werror", + ], + + static_libs: [ + "libbootloader_message", + "libotautil", + "libfs_mgr", + "libbase", + "libcutils", + "liblog", + ], + + init_rc: [ + "uncrypt.rc", + ], +} diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk deleted file mode 100644 index 601f9276e..000000000 --- a/uncrypt/Android.mk +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := uncrypt.cpp -LOCAL_MODULE := uncrypt -LOCAL_STATIC_LIBRARIES := \ - libbootloader_message \ - libotautil \ - libbase \ - liblog \ - libfs_mgr \ - libcutils -LOCAL_CFLAGS := -Wall -Werror -LOCAL_INIT_RC := uncrypt.rc - -include $(BUILD_EXECUTABLE) -- cgit v1.2.3 From 1e0941f4f63d5ee56ad42aa3aa47eb6a8a155077 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 10 Nov 2017 11:49:53 -0800 Subject: applypatch: Change the patch parameter to const Value& in Apply{BSDiff,Image}Patch. It used to be "const Value*", but nullptr won't be a valid input. Test: recovery_host_test; recovery_component_test Change-Id: I904b5689ac3e64504088bf0544c9fb5d45a52243 --- applypatch/applypatch.cpp | 8 ++--- applypatch/bspatch.cpp | 16 ++++----- applypatch/imgpatch.cpp | 53 +++++++++++++----------------- applypatch/include/applypatch/applypatch.h | 15 +++++++-- updater/blockimg.cpp | 4 +-- 5 files changed, 49 insertions(+), 47 deletions(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 2153b5f19..41a72eb15 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -651,11 +651,11 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr int result; if (use_bsdiff) { - result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch.get(), 0, - sink, &ctx); + result = + ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), *patch, 0, sink, &ctx); } else { - result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch.get(), sink, - &ctx, bonus_data); + result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), *patch, sink, &ctx, + bonus_data); } if (result != 0) { diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index c291464a8..e969374be 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -65,7 +65,7 @@ void ShowBSDiffLicense() { ); } -int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, +int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch, size_t patch_offset, SinkFn sink, SHA_CTX* ctx) { auto sha_sink = [&sink, &ctx](const uint8_t* data, size_t len) { len = sink(data, len); @@ -73,22 +73,20 @@ int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value return len; }; - CHECK(patch != nullptr); - CHECK_LE(patch_offset, patch->data.size()); + CHECK_LE(patch_offset, patch.data.size()); int result = bsdiff::bspatch(old_data, old_size, - reinterpret_cast(&patch->data[patch_offset]), - patch->data.size() - patch_offset, sha_sink); + reinterpret_cast(&patch.data[patch_offset]), + patch.data.size() - patch_offset, sha_sink); if (result != 0) { LOG(ERROR) << "bspatch failed, result: " << result; // print SHA1 of the patch in the case of a data error. if (result == 2) { uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast(patch->data.data() + patch_offset), - patch->data.size() - patch_offset, digest); + SHA1(reinterpret_cast(patch.data.data() + patch_offset), + patch.data.size() - patch_offset, digest); std::string patch_sha1 = print_sha1(digest); - LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: " - << patch_sha1; + LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: " << patch_sha1; } } return result; diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 25ba0a182..3682d6115 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -50,7 +50,7 @@ static inline int32_t Read4(const void *address) { // This function is a wrapper of ApplyBSDiffPatch(). It has a custom sink function to deflate the // patched data and stream the deflated data to output. static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len, - const Value* patch, size_t patch_offset, + const Value& patch, size_t patch_offset, const char* deflate_header, SinkFn sink, SHA_CTX* ctx) { size_t expected_target_length = static_cast(Read8(deflate_header + 32)); int level = Read4(deflate_header + 40); @@ -135,48 +135,39 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, size_t patch_size, SinkFn sink) { Value patch(VAL_BLOB, std::string(reinterpret_cast(patch_data), patch_size)); - - return ApplyImagePatch(old_data, old_size, &patch, sink, nullptr, nullptr); + return ApplyImagePatch(old_data, old_size, patch, sink, nullptr, nullptr); } -/* - * Apply the patch given in 'patch_filename' to the source data given - * by (old_data, old_size). Write the patched output to the 'output' - * file, and update the SHA context with the output data as well. - * Return 0 on success. - */ -int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink, SHA_CTX* ctx, const Value* bonus_data) { - if (patch->data.size() < 12) { + if (patch.data.size() < 12) { printf("patch too short to contain header\n"); return -1; } - // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW. - // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and - // CHUNK_GZIP.) - size_t pos = 12; - const char* header = &patch->data[0]; - if (memcmp(header, "IMGDIFF2", 8) != 0) { + // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW. (IMGDIFF1, which is no longer + // supported, used CHUNK_NORMAL and CHUNK_GZIP.) + const char* const patch_header = patch.data.data(); + if (memcmp(patch_header, "IMGDIFF2", 8) != 0) { printf("corrupt patch file header (magic number)\n"); return -1; } - int num_chunks = Read4(header + 8); - + int num_chunks = Read4(patch_header + 8); + size_t pos = 12; for (int i = 0; i < num_chunks; ++i) { // each chunk's header record starts with 4 bytes. - if (pos + 4 > patch->data.size()) { + if (pos + 4 > patch.data.size()) { printf("failed to read chunk %d record\n", i); return -1; } - int type = Read4(&patch->data[pos]); + int type = Read4(patch_header + pos); pos += 4; if (type == CHUNK_NORMAL) { - const char* normal_header = &patch->data[pos]; + const char* normal_header = patch_header + pos; pos += 24; - if (pos > patch->data.size()) { + if (pos > patch.data.size()) { printf("failed to read chunk %d normal header data\n", i); return -1; } @@ -194,30 +185,32 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* return -1; } } else if (type == CHUNK_RAW) { - const char* raw_header = &patch->data[pos]; + const char* raw_header = patch_header + pos; pos += 4; - if (pos > patch->data.size()) { + if (pos > patch.data.size()) { printf("failed to read chunk %d raw header data\n", i); return -1; } size_t data_len = static_cast(Read4(raw_header)); - if (pos + data_len > patch->data.size()) { + if (pos + data_len > patch.data.size()) { printf("failed to read chunk %d raw data\n", i); return -1; } - if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len); - if (sink(reinterpret_cast(&patch->data[pos]), data_len) != data_len) { + if (ctx) { + SHA1_Update(ctx, patch_header + pos, data_len); + } + if (sink(reinterpret_cast(patch_header + pos), data_len) != data_len) { printf("failed to write chunk %d raw data\n", i); return -1; } pos += data_len; } else if (type == CHUNK_DEFLATE) { // deflate chunks have an additional 60 bytes in their chunk header. - const char* deflate_header = &patch->data[pos]; + const char* deflate_header = patch_header + pos; pos += 60; - if (pos > patch->data.size()) { + if (pos > patch.data.size()) { printf("failed to read chunk %d deflate header data\n", i); return -1; } diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index bcb8a4126..6d7ffd78c 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -45,6 +45,7 @@ extern std::string cache_temp_source; using SinkFn = std::function; // applypatch.cpp + int ShowLicenses(); size_t FreeSpaceForFile(const char* filename); int CacheSizeCheck(size_t bytes); @@ -66,15 +67,25 @@ int LoadFileContents(const char* filename, FileContents* file); int SaveFileContents(const char* filename, const FileContents* file); // bspatch.cpp + void ShowBSDiffLicense(); -int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, + +// Applies the bsdiff-patch given in 'patch' (from offset 'patch_offset' to the end) to the source +// data given by (old_data, old_size). Writes the patched output through the given 'sink', and +// updates the SHA-1 context with the output data. Returns 0 on success. +int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch, size_t patch_offset, SinkFn sink, SHA_CTX* ctx); // imgpatch.cpp -int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, + +// Applies the imgdiff-patch given in 'patch' to the source data given by (old_data, old_size), with +// the optional bonus data. Writes the patched output through the given 'sink', and updates the +// SHA-1 context with the output data. Returns 0 on success. +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink, SHA_CTX* ctx, const Value* bonus_data); // freecache.cpp + int MakeFreeSpaceOnCache(size_t bytes_needed); #endif diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 1c931afef..08f9930ea 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1318,7 +1318,7 @@ static int PerformCommandDiff(CommandParameters& params) { RangeSinkWriter writer(params.fd, tgt); if (params.cmdname[0] == 'i') { // imgdiff - if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, + if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value, std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1, std::placeholders::_2), nullptr, nullptr) != 0) { @@ -1327,7 +1327,7 @@ static int PerformCommandDiff(CommandParameters& params) { return -1; } } else { - if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0, + if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value, 0, std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1, std::placeholders::_2), nullptr) != 0) { -- cgit v1.2.3 From 1a8bb0f5421ac9ed7e7de938d2c82c1634abd95e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 1 Nov 2017 13:24:43 -0700 Subject: recovery: format f2fs with encrypt/quota Change-Id: Ia393b7b78b45f09964449ec0e255aa26bb3b8ddf Signed-off-by: Jaegeuk Kim --- roots.cpp | 4 +++- updater/install.cpp | 14 ++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/roots.cpp b/roots.cpp index c0348d715..3cc7b4188 100644 --- a/roots.cpp +++ b/roots.cpp @@ -321,7 +321,9 @@ int format_volume(const char* volume, const char* directory) { } // Has to be f2fs because we checked earlier. - std::vector f2fs_args = { "/sbin/mkfs.f2fs", "-t", "-d1", v->blk_device }; + std::vector f2fs_args = { "/sbin/mkfs.f2fs", "-d1", "-f", + "-O", "encrypt", "-O", "quota", + v->blk_device }; if (length >= 512) { f2fs_args.push_back(std::to_string(length / 512)); } diff --git a/updater/install.cpp b/updater/install.cpp index b865081b4..a111f4b79 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -303,10 +303,16 @@ Value* FormatFn(const char* name, State* state, const std::vector(f2fs_argv)); if (status != 0) { LOG(ERROR) << name << ": mkfs.f2fs failed (" << status << ") on " << location; -- cgit v1.2.3 From e8e4c40246a8c15208b70b4467a0d1790c7ce8fb Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Wed, 8 Nov 2017 14:56:03 -0800 Subject: is_battery_ok use health 2.0 HAL Test: call is_battery_ok in graphics test, and test manually Bug: 69268160 Bug: 63702641 Change-Id: Ifcf4d2e2cb459689c11fc5d1b880bb053aaae8ae --- Android.mk | 15 +++++++- recovery.cpp | 122 ++++++++++++++++++++++++++++++++++------------------------- 2 files changed, 84 insertions(+), 53 deletions(-) diff --git a/Android.mk b/Android.mk index d9966b7cc..50627b9b9 100644 --- a/Android.mk +++ b/Android.mk @@ -147,10 +147,21 @@ endif LOCAL_C_INCLUDES += \ system/vold \ +# Health HAL dependency LOCAL_STATIC_LIBRARIES := \ + android.hardware.health@2.0-impl \ + android.hardware.health@2.0 \ + android.hardware.health@1.0 \ + android.hardware.health@1.0-convert \ + libhidltransport \ + libhidlbase \ + libhwbinder \ + libvndksupport \ + libbatterymonitor + +LOCAL_STATIC_LIBRARIES += \ librecovery \ libverifier \ - libbatterymonitor \ libbootloader_message \ libfs_mgr \ libext4_utils \ @@ -169,8 +180,8 @@ LOCAL_STATIC_LIBRARIES := \ libvintf \ libtinyxml2 \ libbase \ - libcutils \ libutils \ + libcutils \ liblog \ libselinux \ libz diff --git a/recovery.cpp b/recovery.cpp index a89916337..7574065a4 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -50,9 +50,9 @@ #include #include #include /* for property_list */ -#include -#include /* private pmsg functions */ -#include /* for AID_SYSTEM */ +#include +#include /* for AID_SYSTEM */ +#include /* private pmsg functions */ #include #include #include @@ -1260,56 +1260,76 @@ void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity } static bool is_battery_ok() { - struct healthd_config healthd_config = { - .batteryStatusPath = android::String8(android::String8::kEmptyString), - .batteryHealthPath = android::String8(android::String8::kEmptyString), - .batteryPresentPath = android::String8(android::String8::kEmptyString), - .batteryCapacityPath = android::String8(android::String8::kEmptyString), - .batteryVoltagePath = android::String8(android::String8::kEmptyString), - .batteryTemperaturePath = android::String8(android::String8::kEmptyString), - .batteryTechnologyPath = android::String8(android::String8::kEmptyString), - .batteryCurrentNowPath = android::String8(android::String8::kEmptyString), - .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString), - .batteryChargeCounterPath = android::String8(android::String8::kEmptyString), - .batteryFullChargePath = android::String8(android::String8::kEmptyString), - .batteryCycleCountPath = android::String8(android::String8::kEmptyString), - .energyCounter = NULL, - .boot_min_cap = 0, - .screen_on = NULL - }; - healthd_board_init(&healthd_config); + using android::hardware::health::V1_0::BatteryStatus; + using android::hardware::health::V2_0::Result; + using android::hardware::health::V2_0::toString; + using android::hardware::health::V2_0::implementation::Health; + + struct healthd_config healthd_config = { + .batteryStatusPath = android::String8(android::String8::kEmptyString), + .batteryHealthPath = android::String8(android::String8::kEmptyString), + .batteryPresentPath = android::String8(android::String8::kEmptyString), + .batteryCapacityPath = android::String8(android::String8::kEmptyString), + .batteryVoltagePath = android::String8(android::String8::kEmptyString), + .batteryTemperaturePath = android::String8(android::String8::kEmptyString), + .batteryTechnologyPath = android::String8(android::String8::kEmptyString), + .batteryCurrentNowPath = android::String8(android::String8::kEmptyString), + .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString), + .batteryChargeCounterPath = android::String8(android::String8::kEmptyString), + .batteryFullChargePath = android::String8(android::String8::kEmptyString), + .batteryCycleCountPath = android::String8(android::String8::kEmptyString), + .energyCounter = NULL, + .boot_min_cap = 0, + .screen_on = NULL + }; - android::BatteryMonitor monitor; - monitor.init(&healthd_config); + auto health = + android::hardware::health::V2_0::implementation::Health::initInstance(&healthd_config); - int wait_second = 0; - while (true) { - int charge_status = monitor.getChargeStatus(); - // Treat unknown status as charged. - bool charged = (charge_status != android::BATTERY_STATUS_DISCHARGING && - charge_status != android::BATTERY_STATUS_NOT_CHARGING); - android::BatteryProperty capacity; - android::status_t status = monitor.getProperty(android::BATTERY_PROP_CAPACITY, &capacity); - ui_print("charge_status %d, charged %d, status %d, capacity %lld\n", charge_status, - charged, status, capacity.valueInt64); - // At startup, the battery drivers in devices like N5X/N6P take some time to load - // the battery profile. Before the load finishes, it reports value 50 as a fake - // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected - // to finish loading the battery profile earlier than 10 seconds after kernel startup. - if (status == 0 && capacity.valueInt64 == 50) { - if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) { - sleep(1); - wait_second++; - continue; - } - } - // If we can't read battery percentage, it may be a device without battery. In this - // situation, use 100 as a fake battery percentage. - if (status != 0) { - capacity.valueInt64 = 100; - } - return (charged && capacity.valueInt64 >= BATTERY_WITH_CHARGER_OK_PERCENTAGE) || - (!charged && capacity.valueInt64 >= BATTERY_OK_PERCENTAGE); + int wait_second = 0; + while (true) { + auto charge_status = BatteryStatus::UNKNOWN; + health + ->getChargeStatus([&charge_status](auto res, auto out_status) { + if (res == Result::SUCCESS) { + charge_status = out_status; + } + }) + .isOk(); // should not have transport error + + // Treat unknown status as charged. + bool charged = (charge_status != BatteryStatus::DISCHARGING && + charge_status != BatteryStatus::NOT_CHARGING); + + Result res = Result::UNKNOWN; + int32_t capacity = INT32_MIN; + health + ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) { + res = out_res; + capacity = out_capacity; + }) + .isOk(); // should not have transport error + + ui_print("charge_status %d, charged %d, status %s, capacity %" PRId32 "\n", charge_status, + charged, toString(res).c_str(), capacity); + // At startup, the battery drivers in devices like N5X/N6P take some time to load + // the battery profile. Before the load finishes, it reports value 50 as a fake + // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected + // to finish loading the battery profile earlier than 10 seconds after kernel startup. + if (res == Result::SUCCESS && capacity == 50) { + if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) { + sleep(1); + wait_second++; + continue; + } + } + // If we can't read battery percentage, it may be a device without battery. In this + // situation, use 100 as a fake battery percentage. + if (res != Result::SUCCESS) { + capacity = 100; + } + return (charged && capacity >= BATTERY_WITH_CHARGER_OK_PERCENTAGE) || + (!charged && capacity >= BATTERY_OK_PERCENTAGE); } } -- cgit v1.2.3 From f87d20404d624418d7b259f45b32831e54d31ba4 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 15 Nov 2017 11:26:06 -0800 Subject: minadbd: Remove two warning options. '-Wimplicit-function-declaration' is not needed (it's for C89) and already enabled by -Wall. For '-Wno-missing-field-initializers', don't see any existing case that requires the flag. Test: `mmma -j bootable/recovery` on aosp_{bullhead,marlin}-userdebug. Change-Id: I46604723087ed9a7747f6cae31a95fc0074c6758 --- minadbd/Android.mk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 3c9ab3a7f..50e3b34ef 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -16,10 +16,9 @@ LOCAL_PATH:= $(call my-dir) minadbd_cflags := \ -Wall -Werror \ - -Wno-missing-field-initializers \ -DADB_HOST=0 \ -# libadbd (static library) +# libminadbd (static library) # =============================== include $(CLEAR_VARS) @@ -30,7 +29,6 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := libminadbd LOCAL_CFLAGS := $(minadbd_cflags) -LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration LOCAL_C_INCLUDES := bootable/recovery system/core/adb LOCAL_WHOLE_STATIC_LIBRARIES := libadbd LOCAL_STATIC_LIBRARIES := libcrypto libbase -- cgit v1.2.3 From 6e293c99c1c8818fd28edc8dcfc13b5e4f048188 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 15 Nov 2017 16:26:41 -0800 Subject: Switch imgdiff to libbase logging Also add a verbose option. And we won't print messages of 'info' severity unless '-v' is present. Test: run imgdiff and check the logs. Change-Id: I1b90874baea8e72e2a2323a0b63bc5d35e653e6b --- applypatch/imgdiff.cpp | 199 ++++++++++++++------------ applypatch/include/applypatch/imgdiff_image.h | 7 +- 2 files changed, 109 insertions(+), 97 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index f57e7942c..3dae63d4b 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -175,7 +175,7 @@ using android::base::get_unaligned; static constexpr size_t VERSION = 2; // We assume the header "IMGDIFF#" is 8 bytes. -static_assert(VERSION <= 9, "VERSION occupies more than one byte."); +static_assert(VERSION <= 9, "VERSION occupies more than one byte"); static constexpr size_t BLOCK_SIZE = 4096; static constexpr size_t BUFFER_SIZE = 0x8000; @@ -229,8 +229,8 @@ static bool RemoveUsedBlocks(size_t* start, size_t* length, const SortedRangeSet } // TODO find the largest non-overlap chunk. - printf("Removing block %s from %zu - %zu\n", used_ranges.ToString().c_str(), *start, - *start + *length - 1); + LOG(INFO) << "Removing block " << used_ranges.ToString() << " from " << *start << " - " + << *start + *length - 1; // If there's no duplicate entry name, we should only overlap in the head or tail block. Try to // trim both blocks. Skip this source chunk in case it still overlaps with the used ranges. @@ -241,7 +241,7 @@ static bool RemoveUsedBlocks(size_t* start, size_t* length, const SortedRangeSet return true; } - printf("Failed to remove the overlapped block ranges; skip the source\n"); + LOG(WARNING) << "Failed to remove the overlapped block ranges; skip the source"; return false; } @@ -251,6 +251,7 @@ static const struct option OPTIONS[] = { { "block-limit", required_argument, nullptr, 0 }, { "debug-dir", required_argument, nullptr, 0 }, { "split-info", required_argument, nullptr, 0 }, + { "verbose", no_argument, nullptr, 'v' }, { nullptr, 0, nullptr, 0 }, }; @@ -284,6 +285,11 @@ size_t ImageChunk::DataLengthForPatch() const { return raw_data_len_; } +void ImageChunk::Dump(size_t index) const { + LOG(INFO) << "chunk: " << index << ", type: " << type_ << ", start: " << start_ + << ", len: " << DataLengthForPatch() << ", name: " << entry_name_; +} + bool ImageChunk::operator==(const ImageChunk& other) const { if (type_ != other.type_) { return false; @@ -334,7 +340,7 @@ bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src, int fd = mkstemp(ptemp); if (fd == -1) { - printf("MakePatch failed to create a temporary file: %s\n", strerror(errno)); + PLOG(ERROR) << "MakePatch failed to create a temporary file"; return false; } close(fd); @@ -342,18 +348,18 @@ bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src, int r = bsdiff::bsdiff(src.DataForPatch(), src.DataLengthForPatch(), tgt.DataForPatch(), tgt.DataLengthForPatch(), ptemp, bsdiff_cache); if (r != 0) { - printf("bsdiff() failed: %d\n", r); + LOG(ERROR) << "bsdiff() failed: " << r; return false; } android::base::unique_fd patch_fd(open(ptemp, O_RDONLY)); if (patch_fd == -1) { - printf("failed to open %s: %s\n", ptemp, strerror(errno)); + PLOG(ERROR) << "Failed to open " << ptemp; return false; } struct stat st; if (fstat(patch_fd, &st) != 0) { - printf("failed to stat patch file %s: %s\n", ptemp, strerror(errno)); + PLOG(ERROR) << "Failed to stat patch file " << ptemp; return false; } @@ -361,7 +367,7 @@ bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src, patch_data->resize(sz); if (!android::base::ReadFully(patch_fd, patch_data->data(), sz)) { - printf("failed to read \"%s\" %s\n", ptemp, strerror(errno)); + PLOG(ERROR) << "Failed to read " << ptemp; unlink(ptemp); return false; } @@ -373,7 +379,7 @@ bool ImageChunk::MakePatch(const ImageChunk& tgt, const ImageChunk& src, bool ImageChunk::ReconstructDeflateChunk() { if (type_ != CHUNK_DEFLATE) { - printf("attempt to reconstruct non-deflate chunk\n"); + LOG(ERROR) << "Attempted to reconstruct non-deflate chunk"; return false; } @@ -403,7 +409,7 @@ bool ImageChunk::TryReconstruction(int level) { strm.next_in = uncompressed_data_.data(); int ret = deflateInit2(&strm, level, METHOD, WINDOWBITS, MEMLEVEL, STRATEGY); if (ret < 0) { - printf("failed to initialize deflate: %d\n", ret); + LOG(ERROR) << "Failed to initialize deflate: " << ret; return false; } @@ -414,7 +420,7 @@ bool ImageChunk::TryReconstruction(int level) { strm.next_out = buffer.data(); ret = deflate(&strm, Z_FINISH); if (ret < 0) { - printf("failed to deflate: %d\n", ret); + LOG(ERROR) << "Failed to deflate: " << ret; return false; } @@ -490,17 +496,19 @@ size_t PatchChunk::GetHeaderSize() const { } // Return the offset of the next patch into the patch data. -size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset) const { +size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset, size_t index) const { Write4(fd, type_); switch (type_) { case CHUNK_NORMAL: - printf("normal (%10zu, %10zu) %10zu\n", target_start_, target_len_, data_.size()); + LOG(INFO) << android::base::StringPrintf("chunk %zu: normal (%10zu, %10zu) %10zu", index, + target_start_, target_len_, data_.size()); Write8(fd, static_cast(source_start_)); Write8(fd, static_cast(source_len_)); Write8(fd, static_cast(offset)); return offset + data_.size(); case CHUNK_DEFLATE: - printf("deflate (%10zu, %10zu) %10zu\n", target_start_, target_len_, data_.size()); + LOG(INFO) << android::base::StringPrintf("chunk %zu: deflate (%10zu, %10zu) %10zu", index, + target_start_, target_len_, data_.size()); Write8(fd, static_cast(source_start_)); Write8(fd, static_cast(source_len_)); Write8(fd, static_cast(offset)); @@ -513,10 +521,11 @@ size_t PatchChunk::WriteHeaderToFd(int fd, size_t offset) const { Write4(fd, ImageChunk::STRATEGY); return offset + data_.size(); case CHUNK_RAW: - printf("raw (%10zu, %10zu)\n", target_start_, target_len_); + LOG(INFO) << android::base::StringPrintf("chunk %zu: raw (%10zu, %10zu)", index, + target_start_, target_len_); Write4(fd, static_cast(data_.size())); if (!android::base::WriteFully(fd, data_.data(), data_.size())) { - CHECK(false) << "failed to write " << data_.size() << " bytes patch"; + CHECK(false) << "Failed to write " << data_.size() << " bytes patch"; } return offset; default: @@ -545,14 +554,14 @@ bool PatchChunk::WritePatchDataToFd(const std::vector& patch_chunks, // Write out the headers. if (!android::base::WriteStringToFd("IMGDIFF" + std::to_string(VERSION), patch_fd)) { - printf("failed to write \"IMGDIFF%zu\": %s\n", VERSION, strerror(errno)); + PLOG(ERROR) << "Failed to write \"IMGDIFF" << VERSION << "\""; return false; } Write4(patch_fd, static_cast(patch_chunks.size())); + LOG(INFO) << "Writing " << patch_chunks.size() << " patch headers..."; for (size_t i = 0; i < patch_chunks.size(); ++i) { - printf("chunk %zu: ", i); - offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset); + offset = patch_chunks[i].WriteHeaderToFd(patch_fd, offset, i); } // Append each chunk's bsdiff patch, in order. @@ -561,7 +570,7 @@ bool PatchChunk::WritePatchDataToFd(const std::vector& patch_chunks, continue; } if (!android::base::WriteFully(patch_fd, patch.data_.data(), patch.data_.size())) { - printf("failed to write %zu bytes patch to patch_fd\n", patch.data_.size()); + PLOG(ERROR) << "Failed to write " << patch.data_.size() << " bytes patch to patch_fd"; return false; } } @@ -603,10 +612,9 @@ void Image::MergeAdjacentNormalChunks() { void Image::DumpChunks() const { std::string type = is_source_ ? "source" : "target"; - printf("Dumping chunks for %s\n", type.c_str()); + LOG(INFO) << "Dumping chunks for " << type; for (size_t i = 0; i < chunks_.size(); ++i) { - printf("chunk %zu: ", i); - chunks_[i].Dump(); + chunks_[i].Dump(i); } } @@ -615,19 +623,19 @@ bool Image::ReadFile(const std::string& filename, std::vector* file_con android::base::unique_fd fd(open(filename.c_str(), O_RDONLY)); if (fd == -1) { - printf("failed to open \"%s\" %s\n", filename.c_str(), strerror(errno)); + PLOG(ERROR) << "Failed to open " << filename; return false; } struct stat st; if (fstat(fd, &st) != 0) { - printf("failed to stat \"%s\": %s\n", filename.c_str(), strerror(errno)); + PLOG(ERROR) << "Failed to stat " << filename; return false; } size_t sz = static_cast(st.st_size); file_content->resize(sz); if (!android::base::ReadFully(fd, file_content->data(), sz)) { - printf("failed to read \"%s\" %s\n", filename.c_str(), strerror(errno)); + PLOG(ERROR) << "Failed to read " << filename; return false; } fd.reset(); @@ -643,14 +651,14 @@ bool ZipModeImage::Initialize(const std::string& filename) { // Omit the trailing zeros before we pass the file to ziparchive handler. size_t zipfile_size; if (!GetZipFileSize(&zipfile_size)) { - printf("failed to parse the actual size of %s\n", filename.c_str()); + LOG(ERROR) << "Failed to parse the actual size of " << filename; return false; } ZipArchiveHandle handle; int err = OpenArchiveFromMemory(const_cast(file_content_.data()), zipfile_size, filename.c_str(), &handle); if (err != 0) { - printf("failed to open zip file %s: %s\n", filename.c_str(), ErrorCodeString(err)); + LOG(ERROR) << "Failed to open zip file " << filename << ": " << ErrorCodeString(err); CloseArchive(handle); return false; } @@ -669,7 +677,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl void* cookie; int ret = StartIteration(handle, &cookie, nullptr, nullptr); if (ret != 0) { - printf("failed to iterate over entries in %s: %s\n", filename.c_str(), ErrorCodeString(ret)); + LOG(ERROR) << "Failed to iterate over entries in " << filename << ": " << ErrorCodeString(ret); return false; } @@ -685,7 +693,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl } if (ret != -1) { - printf("Error while iterating over zip entries: %s\n", ErrorCodeString(ret)); + LOG(ERROR) << "Error while iterating over zip entries: " << ErrorCodeString(ret); return false; } std::sort(temp_entries.begin(), temp_entries.end(), @@ -697,7 +705,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl if (is_source_) { for (auto& entry : temp_entries) { if (!AddZipEntryToChunks(handle, entry.first, &entry.second)) { - printf("Failed to add %s to source chunks\n", entry.first.c_str()); + LOG(ERROR) << "Failed to add " << entry.first << " to source chunks"; return false; } } @@ -725,7 +733,7 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl // Add the next zip entry. std::string entry_name = temp_entries[nextentry].first; if (!AddZipEntryToChunks(handle, entry_name, &temp_entries[nextentry].second)) { - printf("Failed to add %s to target chunks\n", entry_name.c_str()); + LOG(ERROR) << "Failed to add " << entry_name << " to target chunks"; return false; } @@ -771,8 +779,8 @@ bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::strin std::vector uncompressed_data(uncompressed_len); int ret = ExtractToMemory(handle, entry, uncompressed_data.data(), uncompressed_len); if (ret != 0) { - printf("failed to extract %s with size %zu: %s\n", entry_name.c_str(), uncompressed_len, - ErrorCodeString(ret)); + LOG(ERROR) << "Failed to extract " << entry_name << " with size " << uncompressed_len << ": " + << ErrorCodeString(ret); return false; } ImageChunk curr(CHUNK_DEFLATE, entry->offset, &file_content_, compressed_len, entry_name); @@ -793,7 +801,7 @@ bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::strin // offset 22: comment, n bytes bool ZipModeImage::GetZipFileSize(size_t* input_file_size) { if (file_content_.size() < 22) { - printf("file is too small to be a zip file\n"); + LOG(ERROR) << "File is too small to be a zip file"; return false; } @@ -872,8 +880,8 @@ bool ZipModeImage::CheckAndProcessChunks(ZipModeImage* tgt_image, ZipModeImage* } else if (!tgt_chunk.ReconstructDeflateChunk()) { // We cannot recompress the data and get exactly the same bits as are in the input target // image. Treat the chunk as a normal non-deflated chunk. - printf("failed to reconstruct target deflate chunk [%s]; treating as normal\n", - tgt_chunk.GetEntryName().c_str()); + LOG(WARNING) << "Failed to reconstruct target deflate chunk [" << tgt_chunk.GetEntryName() + << "]; treating as normal"; tgt_chunk.ChangeDeflateChunkToNormal(); src_chunk->ChangeDeflateChunkToNormal(); @@ -902,7 +910,7 @@ bool ZipModeImage::SplitZipModeImageWithLimit(const ZipModeImage& tgt_image, size_t limit = tgt_image.limit_; src_image.DumpChunks(); - printf("Splitting %zu tgt chunks...\n", tgt_image.NumOfChunks()); + LOG(INFO) << "Splitting " << tgt_image.NumOfChunks() << " tgt chunks..."; SortedRangeSet used_src_ranges; // ranges used for previous split source images. @@ -1049,7 +1057,7 @@ void ZipModeImage::ValidateSplitImages(const std::vector& split_tg size_t total_tgt_size) { CHECK_EQ(split_tgt_images.size(), split_src_images.size()); - printf("Validating %zu images\n", split_tgt_images.size()); + LOG(INFO) << "Validating " << split_tgt_images.size() << " images"; // Verify that the target image pieces is continuous and can add up to the total size. size_t last_offset = 0; @@ -1081,7 +1089,7 @@ void ZipModeImage::ValidateSplitImages(const std::vector& split_tg bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image, const ZipModeImage& src_image, std::vector* patch_chunks) { - printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks()); + LOG(INFO) << "Constructing patches for " << tgt_image.NumOfChunks() << " chunks..."; patch_chunks->clear(); bsdiff::SuffixArrayIndexInterface* bsdiff_cache = nullptr; @@ -1103,12 +1111,12 @@ bool ZipModeImage::GeneratePatchesInternal(const ZipModeImage& tgt_image, std::vector patch_data; if (!ImageChunk::MakePatch(tgt_chunk, src_ref, &patch_data, bsdiff_cache_ptr)) { - printf("Failed to generate patch, name: %s\n", tgt_chunk.GetEntryName().c_str()); + LOG(ERROR) << "Failed to generate patch, name: " << tgt_chunk.GetEntryName(); return false; } - printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(), - tgt_chunk.GetRawDataLength()); + LOG(INFO) << "patch " << i << " is " << patch_data.size() << " bytes (of " + << tgt_chunk.GetRawDataLength() << ")"; if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) { patch_chunks->emplace_back(tgt_chunk); @@ -1133,7 +1141,7 @@ bool ZipModeImage::GeneratePatches(const ZipModeImage& tgt_image, const ZipModeI android::base::unique_fd patch_fd( open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); if (patch_fd == -1) { - printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno)); + PLOG(ERROR) << "Failed to open " << patch_name; return false; } @@ -1146,12 +1154,12 @@ bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_im const std::string& patch_name, const std::string& split_info_file, const std::string& debug_dir) { - printf("Construct patches for %zu split images...\n", split_tgt_images.size()); + LOG(INFO) << "Constructing patches for " << split_tgt_images.size() << " split images..."; android::base::unique_fd patch_fd( open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); if (patch_fd == -1) { - printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno)); + PLOG(ERROR) << "Failed to open " << patch_name; return false; } @@ -1160,7 +1168,7 @@ bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_im std::vector patch_chunks; if (!ZipModeImage::GeneratePatchesInternal(split_tgt_images[i], split_src_images[i], &patch_chunks)) { - printf("failed to generate split patch\n"); + LOG(ERROR) << "Failed to generate split patch"; return false; } @@ -1188,12 +1196,12 @@ bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_im open(src_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); if (fd == -1) { - printf("Failed to open %s\n", src_name.c_str()); + PLOG(ERROR) << "Failed to open " << src_name; return false; } if (!android::base::WriteFully(fd, split_src_images[i].PseudoSource().DataForPatch(), split_src_images[i].PseudoSource().DataLengthForPatch())) { - printf("Failed to write split source data into %s\n", src_name.c_str()); + PLOG(ERROR) << "Failed to write split source data into " << src_name; return false; } @@ -1201,7 +1209,7 @@ bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_im fd.reset(open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); if (fd == -1) { - printf("Failed to open %s\n", patch_name.c_str()); + PLOG(ERROR) << "Failed to open " << patch_name; return false; } if (!PatchChunk::WritePatchDataToFd(patch_chunks, fd)) { @@ -1219,8 +1227,7 @@ bool ZipModeImage::GeneratePatches(const std::vector& split_tgt_im std::string split_info_string = android::base::StringPrintf( "%zu\n%zu\n", VERSION, split_info_list.size()) + android::base::Join(split_info_list, '\n'); if (!android::base::WriteStringToFile(split_info_string, split_info_file)) { - printf("failed to write split info to \"%s\": %s\n", split_info_file.c_str(), - strerror(errno)); + PLOG(ERROR) << "Failed to write split info to " << split_info_file; return false; } @@ -1265,7 +1272,7 @@ bool ImageModeImage::Initialize(const std::string& filename) { // not expect zlib headers. int ret = inflateInit2(&strm, -15); if (ret < 0) { - printf("failed to initialize inflate: %d\n", ret); + LOG(ERROR) << "Failed to initialize inflate: " << ret; return false; } @@ -1277,8 +1284,8 @@ bool ImageModeImage::Initialize(const std::string& filename) { strm.next_out = uncompressed_data.data() + uncompressed_len; ret = inflate(&strm, Z_NO_FLUSH); if (ret < 0) { - printf("Warning: inflate failed [%s] at offset [%zu], treating as a normal chunk\n", - strm.msg, chunk_offset); + LOG(WARNING) << "Inflate failed [" << strm.msg << "] at offset [" << chunk_offset + << "]; treating as a normal chunk"; break; } uncompressed_len = allocated - strm.avail_out; @@ -1299,13 +1306,13 @@ bool ImageModeImage::Initialize(const std::string& filename) { // matches the size of the data we got when we actually did the decompression. size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4; if (sz - footer_index < 4) { - printf("Warning: invalid footer position; treating as a nomal chunk\n"); + LOG(WARNING) << "invalid footer position; treating as a normal chunk"; continue; } size_t footer_size = get_unaligned(file_content_.data() + footer_index); if (footer_size != uncompressed_len) { - printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n", - footer_size, uncompressed_len); + LOG(WARNING) << "footer size " << footer_size << " != " << uncompressed_len + << "; treating as a normal chunk"; continue; } @@ -1345,12 +1352,12 @@ bool ImageModeImage::Initialize(const std::string& filename) { bool ImageModeImage::SetBonusData(const std::vector& bonus_data) { CHECK(is_source_); if (chunks_.size() < 2 || !chunks_[1].SetBonusData(bonus_data)) { - printf("Failed to set bonus data\n"); + LOG(ERROR) << "Failed to set bonus data"; DumpChunks(); return false; } - printf(" using %zu bytes of bonus data\n", bonus_data.size()); + LOG(INFO) << " using " << bonus_data.size() << " bytes of bonus data"; return true; } @@ -1362,14 +1369,14 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI src_image->MergeAdjacentNormalChunks(); if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) { - printf("source and target don't have same number of chunks!\n"); + LOG(ERROR) << "Source and target don't have same number of chunks!"; tgt_image->DumpChunks(); src_image->DumpChunks(); return false; } for (size_t i = 0; i < tgt_image->NumOfChunks(); ++i) { if ((*tgt_image)[i].GetType() != (*src_image)[i].GetType()) { - printf("source and target don't have same chunk structure! (chunk %zu)\n", i); + LOG(ERROR) << "Source and target don't have same chunk structure! (chunk " << i << ")"; tgt_image->DumpChunks(); src_image->DumpChunks(); return false; @@ -1390,8 +1397,8 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI } else if (!tgt_chunk.ReconstructDeflateChunk()) { // We cannot recompress the data and get exactly the same bits as are in the input target // image, fall back to normal - printf("failed to reconstruct target deflate chunk %zu [%s]; treating as normal\n", i, - tgt_chunk.GetEntryName().c_str()); + LOG(WARNING) << "Failed to reconstruct target deflate chunk " << i << " [" + << tgt_chunk.GetEntryName() << "]; treating as normal"; tgt_chunk.ChangeDeflateChunkToNormal(); src_chunk.ChangeDeflateChunkToNormal(); } @@ -1403,7 +1410,7 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI src_image->MergeAdjacentNormalChunks(); if (tgt_image->NumOfChunks() != src_image->NumOfChunks()) { // This shouldn't happen. - printf("merging normal chunks went awry\n"); + LOG(ERROR) << "Merging normal chunks went awry"; return false; } @@ -1415,7 +1422,7 @@ bool ImageModeImage::CheckAndProcessChunks(ImageModeImage* tgt_image, ImageModeI bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image, const ImageModeImage& src_image, const std::string& patch_name) { - printf("Construct patches for %zu chunks...\n", tgt_image.NumOfChunks()); + LOG(INFO) << "Constructing patches for " << tgt_image.NumOfChunks() << " chunks..."; std::vector patch_chunks; patch_chunks.reserve(tgt_image.NumOfChunks()); @@ -1430,11 +1437,11 @@ bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image, std::vector patch_data; if (!ImageChunk::MakePatch(tgt_chunk, src_chunk, &patch_data, nullptr)) { - printf("Failed to generate patch for target chunk %zu: ", i); + LOG(ERROR) << "Failed to generate patch for target chunk " << i; return false; } - printf("patch %3zu is %zu bytes (of %zu)\n", i, patch_data.size(), - tgt_chunk.GetRawDataLength()); + LOG(INFO) << "patch " << i << " is " << patch_data.size() << " bytes (of " + << tgt_chunk.GetRawDataLength() << ")"; if (PatchChunk::RawDataIsSmaller(tgt_chunk, patch_data.size())) { patch_chunks.emplace_back(tgt_chunk); @@ -1448,7 +1455,7 @@ bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image, android::base::unique_fd patch_fd( open(patch_name.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)); if (patch_fd == -1) { - printf("failed to open \"%s\": %s\n", patch_name.c_str(), strerror(errno)); + PLOG(ERROR) << "Failed to open " << patch_name; return false; } @@ -1456,6 +1463,7 @@ bool ImageModeImage::GeneratePatches(const ImageModeImage& tgt_image, } int imgdiff(int argc, const char** argv) { + bool verbose = false; bool zip_mode = false; std::vector bonus_data; size_t blocks_limit = 0; @@ -1464,9 +1472,10 @@ int imgdiff(int argc, const char** argv) { int opt; int option_index; - optind = 1; // Reset the getopt state so that we can call it multiple times for test. + optind = 0; // Reset the getopt state so that we can call it multiple times for test. - while ((opt = getopt_long(argc, const_cast(argv), "zb:", OPTIONS, &option_index)) != -1) { + while ((opt = getopt_long(argc, const_cast(argv), "zb:v", OPTIONS, &option_index)) != + -1) { switch (opt) { case 'z': zip_mode = true; @@ -1474,27 +1483,30 @@ int imgdiff(int argc, const char** argv) { case 'b': { android::base::unique_fd fd(open(optarg, O_RDONLY)); if (fd == -1) { - printf("failed to open bonus file %s: %s\n", optarg, strerror(errno)); + PLOG(ERROR) << "Failed to open bonus file " << optarg; return 1; } struct stat st; if (fstat(fd, &st) != 0) { - printf("failed to stat bonus file %s: %s\n", optarg, strerror(errno)); + PLOG(ERROR) << "Failed to stat bonus file " << optarg; return 1; } size_t bonus_size = st.st_size; bonus_data.resize(bonus_size); if (!android::base::ReadFully(fd, bonus_data.data(), bonus_size)) { - printf("failed to read bonus file %s: %s\n", optarg, strerror(errno)); + PLOG(ERROR) << "Failed to read bonus file " << optarg; return 1; } break; } + case 'v': + verbose = true; + break; case 0: { std::string name = OPTIONS[option_index].name; if (name == "block-limit" && !android::base::ParseUint(optarg, &blocks_limit)) { - printf("failed to parse size blocks_limit: %s\n", optarg); + LOG(ERROR) << "Failed to parse size blocks_limit: " << optarg; return 1; } else if (name == "split-info") { split_info_file = optarg; @@ -1504,22 +1516,28 @@ int imgdiff(int argc, const char** argv) { break; } default: - printf("unexpected opt: %s\n", optarg); + LOG(ERROR) << "unexpected opt: " << static_cast(opt); return 2; } } + if (!verbose) { + android::base::SetMinimumLogSeverity(android::base::WARNING); + } + if (argc - optind != 3) { - printf("usage: %s [options] \n", argv[0]); - printf( - " -z , Generate patches in zip mode, src and tgt should be zip files.\n" - " -b , Bonus file in addition to src, image mode only.\n" - " --block-limit, For large zips, split the src and tgt based on the block limit;\n" - " and generate patches between each pair of pieces. Concatenate these\n" - " patches together and output them into .\n" - " --split-info, Output the split information (patch_size, tgt_size, src_ranges);\n" - " zip mode with block-limit only.\n" - " --debug_dir, Debug directory to put the split srcs and patches, zip mode only.\n"); + LOG(ERROR) << "usage: " << argv[0] << " [options] "; + LOG(ERROR) + << " -z , Generate patches in zip mode, src and tgt should be zip files.\n" + " -b , Bonus file in addition to src, image mode only.\n" + " --block-limit, For large zips, split the src and tgt based on the block limit;\n" + " and generate patches between each pair of pieces. Concatenate " + "these\n" + " patches together and output them into .\n" + " --split-info, Output the split information (patch_size, tgt_size, src_ranges);\n" + " zip mode with block-limit only.\n" + " --debug_dir, Debug directory to put the split srcs and patches, zip mode only.\n" + " -v, --verbose, Enable verbose logging."; return 2; } @@ -1538,14 +1556,11 @@ int imgdiff(int argc, const char** argv) { return 1; } - // TODO save and output the split information so that caller can create split transfer lists - // accordingly. - // Compute bsdiff patches for each chunk's data (the uncompressed data, in the case of // deflate chunks). if (blocks_limit > 0) { if (split_info_file.empty()) { - printf("split-info path cannot be empty when generating patches with a block-limit.\n"); + LOG(ERROR) << "split-info path cannot be empty when generating patches with a block-limit"; return 1; } diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h index 00a84f3a9..0f74420f0 100644 --- a/applypatch/include/applypatch/imgdiff_image.h +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -62,10 +62,7 @@ class ImageChunk { const uint8_t* DataForPatch() const; size_t DataLengthForPatch() const; - void Dump() const { - printf("type: %d, start: %zu, len: %zu, name: %s\n", type_, start_, DataLengthForPatch(), - entry_name_.c_str()); - } + void Dump(size_t index) const; void SetUncompressedData(std::vector data); bool SetBonusData(const std::vector& bonus_data); @@ -140,7 +137,7 @@ class PatchChunk { private: size_t GetHeaderSize() const; - size_t WriteHeaderToFd(int fd, size_t offset) const; + size_t WriteHeaderToFd(int fd, size_t offset, size_t index) const; // The patch chunk type is the same as the target chunk type. The only exception is we change // the |type_| to CHUNK_RAW if target length is smaller than the patch size. -- cgit v1.2.3 From cc10008887e2c6d8359af1d08c0bce800e8cb8c0 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 17 Nov 2017 23:53:22 -0800 Subject: root: Fix an issue when volume length from fs_mgr is negative. fs_mgr passes negative length as offset for crypt_footer. We need to compute the actual device size for this case in addition to when length is zero. Bug: 68949069 Change-Id: I803955dd9591ad7752cee0dea9b683be8f4fd4ba --- roots.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/roots.cpp b/roots.cpp index c0348d715..47e9a54e3 100644 --- a/roots.cpp +++ b/roots.cpp @@ -262,17 +262,20 @@ int format_volume(const char* volume, const char* directory) { } int64_t length = 0; - if (v->length != 0) { + if (v->length > 0) { length = v->length; - } else if (v->key_loc != nullptr && strcmp(v->key_loc, "footer") == 0) { + } else if (v->length < 0 || + (v->key_loc != nullptr && strcmp(v->key_loc, "footer") == 0)) { android::base::unique_fd fd(open(v->blk_device, O_RDONLY)); if (fd == -1) { PLOG(ERROR) << "format_volume: failed to open " << v->blk_device; return -1; } - length = get_file_size(fd.get(), CRYPT_FOOTER_OFFSET); + length = + get_file_size(fd.get(), v->length ? -v->length : CRYPT_FOOTER_OFFSET); if (length <= 0) { - LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device; + LOG(ERROR) << "get_file_size: invalid size " << length << " for " + << v->blk_device; return -1; } } -- cgit v1.2.3 From 99b73be3a8f3a97ff11f3e50a4169292a2f36fb1 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 28 Nov 2017 17:23:06 -0800 Subject: Detect interrupted update due to power off An interrupted update may stash extra blocks in /cache, leading to a failure when checking the cache size. We can save the incremented retry_count in the BCB before installing the update; and distinguish a fresh update from an interrupted one this way. Bug: 68679601 Test: An interrupted update reapplies successfully. Change-Id: Ic1403e1fd25a937c91ef34c14b92a0f6c8f1c0f4 --- recovery.cpp | 565 +++++++++++++++++++++++++++------------------------- updater/install.cpp | 6 +- 2 files changed, 302 insertions(+), 269 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index a89916337..6bd291463 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1313,6 +1313,7 @@ static bool is_battery_ok() { } } +// Set the retry count to |retry_count| in BCB. static void set_retry_bootloader_message(int retry_count, const std::vector& args) { std::vector options; for (const auto& arg : args) { @@ -1321,8 +1322,8 @@ static void set_retry_bootloader_message(int retry_count, const std::vector args = get_args(argc, argv); - std::vector args_to_parse(args.size()); - std::transform(args.cbegin(), args.cend(), args_to_parse.begin(), - [](const std::string& arg) { return const_cast(arg.c_str()); }); - - const char *update_package = NULL; - bool should_wipe_data = false; - bool should_prompt_and_wipe_data = false; - bool should_wipe_cache = false; - bool should_wipe_ab = false; - size_t wipe_package_size = 0; - bool show_text = false; - bool sideload = false; - bool sideload_auto_reboot = false; - bool just_exit = false; - bool shutdown_after = false; - int retry_count = 0; - bool security_update = false; - - int arg; - int option_index; - while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS, - &option_index)) != -1) { - switch (arg) { - case 'n': android::base::ParseInt(optarg, &retry_count, 0); break; - case 'u': update_package = optarg; break; - case 'w': should_wipe_data = true; break; - case 'c': should_wipe_cache = true; break; - case 't': show_text = true; break; - case 's': sideload = true; break; - case 'a': sideload = true; sideload_auto_reboot = true; break; - case 'x': just_exit = true; break; - case 'l': locale = optarg; break; - case 'p': shutdown_after = true; break; - case 'r': reason = optarg; break; - case 'e': security_update = true; break; - case 0: { - std::string option = OPTIONS[option_index].name; - if (option == "wipe_ab") { - should_wipe_ab = true; - } else if (option == "wipe_package_size") { - android::base::ParseUint(optarg, &wipe_package_size); - } else if (option == "prompt_and_wipe_data") { - should_prompt_and_wipe_data = true; - } - break; - } - case '?': - LOG(ERROR) << "Invalid command argument"; - continue; + // We don't have logcat yet under recovery; so we'll print error on screen and + // log to stdout (which is redirected to recovery.log) as we used to do. + android::base::InitLogging(argv, &UiLogger); + + // Take last pmsg contents and rewrite it to the current pmsg session. + static const char filter[] = "recovery/"; + // Do we need to rotate? + bool doRotate = false; + + __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logbasename, &doRotate); + // Take action to refresh pmsg contents + __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &doRotate); + + // 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 instances with different timestamps. + redirect_stdio(TEMPORARY_LOG_FILE); + + printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); + + load_volume_table(); + has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr; + + std::vector args = get_args(argc, argv); + std::vector args_to_parse(args.size()); + std::transform(args.cbegin(), args.cend(), args_to_parse.begin(), + [](const std::string& arg) { return const_cast(arg.c_str()); }); + + const char* update_package = nullptr; + bool should_wipe_data = false; + bool should_prompt_and_wipe_data = false; + bool should_wipe_cache = false; + bool should_wipe_ab = false; + size_t wipe_package_size = 0; + bool show_text = false; + bool sideload = false; + bool sideload_auto_reboot = false; + bool just_exit = false; + bool shutdown_after = false; + int retry_count = 0; + bool security_update = false; + + int arg; + int option_index; + while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS, + &option_index)) != -1) { + switch (arg) { + case 'n': + android::base::ParseInt(optarg, &retry_count, 0); + break; + case 'u': + update_package = optarg; + break; + case 'w': + should_wipe_data = true; + break; + case 'c': + should_wipe_cache = true; + break; + case 't': + show_text = true; + break; + case 's': + sideload = true; + break; + case 'a': + sideload = true; + sideload_auto_reboot = true; + break; + case 'x': + just_exit = true; + break; + case 'l': + locale = optarg; + break; + case 'p': + shutdown_after = true; + break; + case 'r': + reason = optarg; + break; + case 'e': + security_update = true; + break; + case 0: { + std::string option = OPTIONS[option_index].name; + if (option == "wipe_ab") { + should_wipe_ab = true; + } else if (option == "wipe_package_size") { + android::base::ParseUint(optarg, &wipe_package_size); + } else if (option == "prompt_and_wipe_data") { + should_prompt_and_wipe_data = true; } + break; + } + case '?': + LOG(ERROR) << "Invalid command argument"; + continue; } + } - if (locale.empty()) { - if (has_cache) { - locale = load_locale_from_cache(); - } + if (locale.empty()) { + if (has_cache) { + locale = load_locale_from_cache(); + } - if (locale.empty()) { - locale = DEFAULT_LOCALE; - } + if (locale.empty()) { + locale = DEFAULT_LOCALE; } + } - printf("locale is [%s]\n", locale.c_str()); - printf("stage is [%s]\n", stage.c_str()); - printf("reason is [%s]\n", reason); + printf("locale is [%s]\n", locale.c_str()); + printf("stage is [%s]\n", stage.c_str()); + printf("reason is [%s]\n", reason); - Device* device = make_device(); - if (android::base::GetBoolProperty("ro.boot.quiescent", false)) { - printf("Quiescent recovery mode.\n"); - ui = new StubRecoveryUI(); - } else { - ui = device->GetUI(); + Device* device = make_device(); + if (android::base::GetBoolProperty("ro.boot.quiescent", false)) { + printf("Quiescent recovery mode.\n"); + ui = new StubRecoveryUI(); + } else { + ui = device->GetUI(); - if (!ui->Init(locale)) { - printf("Failed to initialize UI, use stub UI instead.\n"); - ui = new StubRecoveryUI(); - } + if (!ui->Init(locale)) { + printf("Failed to initialize UI, use stub UI instead.\n"); + ui = new StubRecoveryUI(); } + } - // Set background string to "installing security update" for security update, - // otherwise set it to "installing system update". - ui->SetSystemUpdateText(security_update); + // Set background string to "installing security update" for security update, + // otherwise set it to "installing system update". + ui->SetSystemUpdateText(security_update); - int st_cur, st_max; - if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) { - ui->SetStage(st_cur, st_max); - } + int st_cur, st_max; + if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) { + ui->SetStage(st_cur, st_max); + } - ui->SetBackground(RecoveryUI::NONE); - if (show_text) ui->ShowText(true); + ui->SetBackground(RecoveryUI::NONE); + if (show_text) ui->ShowText(true); - sehandle = selinux_android_file_context_handle(); - selinux_android_set_sehandle(sehandle); - if (!sehandle) { - ui->Print("Warning: No file_contexts\n"); - } + sehandle = selinux_android_file_context_handle(); + selinux_android_set_sehandle(sehandle); + if (!sehandle) { + ui->Print("Warning: No file_contexts\n"); + } - device->StartRecovery(); + device->StartRecovery(); - printf("Command:"); - for (const auto& arg : args) { - printf(" \"%s\"", arg.c_str()); - } - printf("\n\n"); + printf("Command:"); + for (const auto& arg : args) { + printf(" \"%s\"", arg.c_str()); + } + printf("\n\n"); - property_list(print_property, NULL); - printf("\n"); + property_list(print_property, nullptr); + printf("\n"); - ui->Print("Supported API: %d\n", kRecoveryApiVersion); + ui->Print("Supported API: %d\n", kRecoveryApiVersion); - int status = INSTALL_SUCCESS; + int status = INSTALL_SUCCESS; - if (update_package != NULL) { - // It's not entirely true that we will modify the flash. But we want - // to log the update attempt since update_package is non-NULL. - modified_flash = true; + if (update_package != nullptr) { + // It's not entirely true that we will modify the flash. But we want + // to log the update attempt since update_package is non-NULL. + modified_flash = true; - if (!is_battery_ok()) { - ui->Print("battery capacity is not enough for installing package, needed is %d%%\n", - BATTERY_OK_PERCENTAGE); - // Log the error code to last_install when installation skips due to - // low battery. - log_failure_code(kLowBattery, update_package); - status = INSTALL_SKIPPED; - } else if (bootreason_in_blacklist()) { - // Skip update-on-reboot when bootreason is kernel_panic or similar - ui->Print("bootreason is in the blacklist; skip OTA installation\n"); - log_failure_code(kBootreasonInBlacklist, update_package); - status = INSTALL_SKIPPED; - } else { - status = install_package(update_package, &should_wipe_cache, - TEMPORARY_INSTALL_FILE, true, retry_count); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - wipe_cache(false, device); - } - if (status != INSTALL_SUCCESS) { - ui->Print("Installation aborted.\n"); - // When I/O error happens, reboot and retry installation RETRY_LIMIT - // times before we abandon this OTA update. - if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) { - copy_logs(); - set_retry_bootloader_message(retry_count, args); - // Print retry count on screen. - ui->Print("Retry attempt %d\n", retry_count); - - // Reboot and retry the update - if (!reboot("reboot,recovery")) { - ui->Print("Reboot failed\n"); - } else { - while (true) { - pause(); - } - } - } - // If this is an eng or userdebug build, then automatically - // turn the text display on if the script fails so the error - // message is visible. - if (is_ro_debuggable()) { - ui->ShowText(true); - } - } - } - } else if (should_wipe_data) { - if (!wipe_data(device)) { - status = INSTALL_ERROR; - } - } else if (should_prompt_and_wipe_data) { - ui->ShowText(true); - ui->SetBackground(RecoveryUI::ERROR); - if (!prompt_and_wipe_data(device)) { - status = INSTALL_ERROR; - } - ui->ShowText(false); - } else if (should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; - } - } else if (should_wipe_ab) { - if (!wipe_ab_device(wipe_package_size)) { - status = INSTALL_ERROR; - } - } else if (sideload) { - // 'adb reboot sideload' acts the same as user presses key combinations - // to enter the sideload mode. When 'sideload-auto-reboot' is used, text - // display will NOT be turned on by default. And it will reboot after - // sideload finishes even if there are errors. Unless one turns on the - // text display during the installation. This is to enable automated - // testing. - if (!sideload_auto_reboot) { - ui->ShowText(true); - } - status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; + if (!is_battery_ok()) { + ui->Print("battery capacity is not enough for installing package, needed is %d%%\n", + BATTERY_OK_PERCENTAGE); + // Log the error code to last_install when installation skips due to + // low battery. + log_failure_code(kLowBattery, update_package); + status = INSTALL_SKIPPED; + } else if (bootreason_in_blacklist()) { + // Skip update-on-reboot when bootreason is kernel_panic or similar + ui->Print("bootreason is in the blacklist; skip OTA installation\n"); + log_failure_code(kBootreasonInBlacklist, update_package); + status = INSTALL_SKIPPED; + } else { + // It's a fresh update. Initialize the retry_count in the BCB to 1; therefore we can later + // identify the interrupted update due to unexpected reboots. + if (retry_count == 0) { + set_retry_bootloader_message(retry_count + 1, args); + } + + status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, true, + retry_count); + if (status == INSTALL_SUCCESS && should_wipe_cache) { + wipe_cache(false, device); + } + if (status != INSTALL_SUCCESS) { + ui->Print("Installation aborted.\n"); + // When I/O error happens, reboot and retry installation RETRY_LIMIT + // times before we abandon this OTA update. + if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) { + copy_logs(); + retry_count += 1; + set_retry_bootloader_message(retry_count, args); + // Print retry count on screen. + ui->Print("Retry attempt %d\n", retry_count); + + // Reboot and retry the update + if (!reboot("reboot,recovery")) { + ui->Print("Reboot failed\n"); + } else { + while (true) { + pause(); } + } } - ui->Print("\nInstall from ADB complete (status: %d).\n", status); - if (sideload_auto_reboot) { - ui->Print("Rebooting automatically.\n"); + // If this is an eng or userdebug build, then automatically + // turn the text display on if the script fails so the error + // message is visible. + if (is_ro_debuggable()) { + ui->ShowText(true); } - } else if (!just_exit) { - // If this is an eng or userdebug build, automatically turn on the text display if no command - // is specified. Note that this should be called before setting the background to avoid - // flickering the background image. - if (is_ro_debuggable()) { - ui->ShowText(true); } - status = INSTALL_NONE; // No command specified - ui->SetBackground(RecoveryUI::NO_COMMAND); } + } else if (should_wipe_data) { + if (!wipe_data(device)) { + status = INSTALL_ERROR; + } + } else if (should_prompt_and_wipe_data) { + ui->ShowText(true); + ui->SetBackground(RecoveryUI::ERROR); + if (!prompt_and_wipe_data(device)) { + status = INSTALL_ERROR; + } + ui->ShowText(false); + } else if (should_wipe_cache) { + if (!wipe_cache(false, device)) { + status = INSTALL_ERROR; + } + } else if (should_wipe_ab) { + if (!wipe_ab_device(wipe_package_size)) { + status = INSTALL_ERROR; + } + } else if (sideload) { + // 'adb reboot sideload' acts the same as user presses key combinations + // to enter the sideload mode. When 'sideload-auto-reboot' is used, text + // display will NOT be turned on by default. And it will reboot after + // sideload finishes even if there are errors. Unless one turns on the + // text display during the installation. This is to enable automated + // testing. + if (!sideload_auto_reboot) { + ui->ShowText(true); + } + status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE); + if (status == INSTALL_SUCCESS && should_wipe_cache) { + if (!wipe_cache(false, device)) { + status = INSTALL_ERROR; + } + } + ui->Print("\nInstall from ADB complete (status: %d).\n", status); + if (sideload_auto_reboot) { + ui->Print("Rebooting automatically.\n"); + } + } else if (!just_exit) { + // If this is an eng or userdebug build, automatically turn on the text display if no command + // is specified. Note that this should be called before setting the background to avoid + // flickering the background image. + if (is_ro_debuggable()) { + ui->ShowText(true); + } + status = INSTALL_NONE; // No command specified + ui->SetBackground(RecoveryUI::NO_COMMAND); + } - if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { - ui->SetBackground(RecoveryUI::ERROR); - if (!ui->IsTextVisible()) { - sleep(5); - } + if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { + ui->SetBackground(RecoveryUI::ERROR); + if (!ui->IsTextVisible()) { + sleep(5); } + } - Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; - // 1. If the recovery menu is visible, prompt and wait for commands. - // 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into - // recovery to sideload a package.) - // 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device - // without waiting. - // 4. In all other cases, reboot the device. Therefore, normal users will observe the device - // reboot after it shows the "error" screen for 5s. - if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) { - Device::BuiltinAction temp = prompt_and_wait(device, status); - if (temp != Device::NO_ACTION) { - after = temp; - } + Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; + // 1. If the recovery menu is visible, prompt and wait for commands. + // 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into + // recovery to sideload a package.) + // 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device + // without waiting. + // 4. In all other cases, reboot the device. Therefore, normal users will observe the device + // reboot after it shows the "error" screen for 5s. + if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) { + Device::BuiltinAction temp = prompt_and_wait(device, status); + if (temp != Device::NO_ACTION) { + after = temp; } + } - // Save logs and clean up before rebooting or shutting down. - finish_recovery(); + // Save logs and clean up before rebooting or shutting down. + finish_recovery(); - switch (after) { - case Device::SHUTDOWN: - ui->Print("Shutting down...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); - break; + switch (after) { + case Device::SHUTDOWN: + ui->Print("Shutting down...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); + break; - case Device::REBOOT_BOOTLOADER: - ui->Print("Rebooting to bootloader...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); - break; + case Device::REBOOT_BOOTLOADER: + ui->Print("Rebooting to bootloader...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); + break; - default: - ui->Print("Rebooting...\n"); - reboot("reboot,"); - break; - } - while (true) { - pause(); - } - // Should be unreachable. - return EXIT_SUCCESS; + default: + ui->Print("Rebooting...\n"); + reboot("reboot,"); + break; + } + while (true) { + pause(); + } + // Should be unreachable. + return EXIT_SUCCESS; } diff --git a/updater/install.cpp b/updater/install.cpp index a111f4b79..870b85791 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -569,7 +569,11 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, const std::vectoris_retry || CacheSizeCheck(bytes) == 0) { + return StringValue("t"); + } + return StringValue(""); } // apply_patch(src_file, tgt_file, tgt_sha1, tgt_size, patch1_sha1, patch1_blob, [...]) -- cgit v1.2.3 From c1c7311b8ada7fce948afaabe91d72f4a7c0cb68 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 28 Nov 2017 19:48:05 -0800 Subject: add sload.f2fs for recovery format Change-Id: Iddfe54b2b36f2d531925cbe61c98dbfb4903c0d1 Signed-off-by: Jaegeuk Kim --- Android.mk | 2 +- roots.cpp | 19 +++++++++++++------ updater/install.cpp | 9 +++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Android.mk b/Android.mk index d9966b7cc..2f57db344 100644 --- a/Android.mk +++ b/Android.mk @@ -89,7 +89,7 @@ LOCAL_REQUIRED_MODULES := e2fsdroid_static mke2fs_static mke2fs.conf ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) ifeq ($(HOST_OS),linux) -LOCAL_REQUIRED_MODULES += mkfs.f2fs +LOCAL_REQUIRED_MODULES += sload.f2fs mkfs.f2fs endif endif diff --git a/roots.cpp b/roots.cpp index eb299ad6b..e2d5d6543 100644 --- a/roots.cpp +++ b/roots.cpp @@ -324,16 +324,23 @@ int format_volume(const char* volume, const char* directory) { } // Has to be f2fs because we checked earlier. - std::vector f2fs_args = { "/sbin/mkfs.f2fs", "-d1", "-f", - "-O", "encrypt", "-O", "quota", - v->blk_device }; + std::string cmd("/sbin/mkfs.f2fs"); + std::vector make_f2fs_cmd = { cmd, "-d1", "-f", "-O", + "encrypt", "-O", "quota", v->blk_device }; if (length >= 512) { - f2fs_args.push_back(std::to_string(length / 512)); + make_f2fs_cmd.push_back(std::to_string(length / 512)); } - int result = exec_cmd(f2fs_args); + int result = exec_cmd(make_f2fs_cmd); + if (result == 0 && directory != nullptr) { + cmd = "/sbin/sload.f2fs"; + std::vector sload_f2fs_cmd = { + cmd, "-f", directory, "-t", volume, v->blk_device, + }; + result = exec_cmd(sload_f2fs_cmd); + } if (result != 0) { - PLOG(ERROR) << "format_volume: Failed to make f2fs on " << v->blk_device; + PLOG(ERROR) << "format_volume: Failed " << cmd << " on " << v->blk_device; return -1; } return 0; diff --git a/updater/install.cpp b/updater/install.cpp index 870b85791..b83d30ff3 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -318,6 +318,15 @@ Value* FormatFn(const char* name, State* state, const std::vector(sload_argv)); + if (status != 0) { + LOG(ERROR) << name << ": sload.f2fs failed (" << status << ") on " << location; + return StringValue(""); + } + return StringValue(location); } else { LOG(ERROR) << name << ": unsupported fs_type \"" << fs_type << "\" partition_type \"" -- cgit v1.2.3 From 4dca6f12dbbf76917c03514f8b7123a4ec927984 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 6 Dec 2017 08:41:50 -0800 Subject: Add a /bin symlink for consistency. Bug: http://b/63142920 Test: builds Change-Id: I1f96935daca4d79f753e172067e07f8e27ea819e --- etc/init.rc | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/init.rc b/etc/init.rc index d8121cc4e..0fc6c4c13 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -11,6 +11,7 @@ on init export ANDROID_DATA /data export EXTERNAL_STORAGE /sdcard + symlink /system/bin /bin symlink /system/etc /etc mount cgroup none /acct cpuacct -- cgit v1.2.3 From 47e5a8d08569a456981854a194d6dc66b8d85265 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 1 Dec 2017 10:53:34 -0800 Subject: applypatch: Remove the 'st' field from FileContents. It used to keep track of the stat(2) info (e.g. st_mode/st_gid/st_uid) while patching a file in file-based OTA. Test: Build and use the new updater to apply an update on bullhead. Change-Id: Ibf8f0f4b14298a9489bf24a2678bb279c5d9c8f3 --- applypatch/applypatch.cpp | 18 +++--------------- applypatch/include/applypatch/applypatch.h | 2 -- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 41a72eb15..04b964b17 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -58,12 +58,13 @@ int LoadFileContents(const char* filename, FileContents* file) { return LoadPartitionContents(filename, file); } - if (stat(filename, &file->st) == -1) { + struct stat sb; + if (stat(filename, &sb) == -1) { printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); return -1; } - std::vector data(file->st.st_size); + std::vector data(sb.st_size); unique_file f(ota_fopen(filename, "rb")); if (!f) { printf("failed to open \"%s\": %s\n", filename, strerror(errno)); @@ -180,10 +181,6 @@ static int LoadPartitionContents(const std::string& filename, FileContents* file buffer.resize(buffer_size); file->data = std::move(buffer); - // Fake some stat() info. - file->st.st_mode = 0644; - file->st.st_uid = 0; - file->st.st_gid = 0; return 0; } @@ -212,15 +209,6 @@ int SaveFileContents(const char* filename, const FileContents* file) { return -1; } - if (chmod(filename, file->st.st_mode) != 0) { - printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno)); - return -1; - } - if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) { - printf("chown of \"%s\" failed: %s\n", filename, strerror(errno)); - return -1; - } - return 0; } diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 6d7ffd78c..c8ad91505 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -18,7 +18,6 @@ #define _APPLYPATCH_H #include -#include #include #include @@ -33,7 +32,6 @@ struct Value; struct FileContents { uint8_t sha1[SHA_DIGEST_LENGTH]; std::vector data; - struct stat st; }; // When there isn't enough room on the target filesystem to hold the patched version of the file, -- cgit v1.2.3 From 26fd78c024a4f6eadf012ea83ed5d2cc0f2dcfdf Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Wed, 13 Dec 2017 17:27:12 +0800 Subject: Let update_verifier work on non-AB update devices Make update_verifier check if it runs on A/B update devices at the beginning, and quit immediately if it doesn't, instead of re-boot. Bug: 70541023 Test: On aosp/master: $ lunch aosp_x86_64-userdebug; m -j # boot to home screen # On goog/master: $ lunch aosp_walleye-userdebug; m -j # boot to home screen Change-Id: Ib71a3a3b272cfa5dd0b479eaa067eedaec8fde7d --- update_verifier/update_verifier_main.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/update_verifier/update_verifier_main.cpp b/update_verifier/update_verifier_main.cpp index 9dd5a0cc4..a86203bfb 100644 --- a/update_verifier/update_verifier_main.cpp +++ b/update_verifier/update_verifier_main.cpp @@ -16,11 +16,20 @@ // See the comments in update_verifier.cpp. +#include + #include +#include #include "update_verifier/update_verifier.h" int main(int argc, char** argv) { + std::string s = android::base::GetProperty("ro.boot.slot_suffix", ""); + + if (s.empty()) { + return 0; // non-A/B update device, so we quit + } + // Set up update_verifier logging to be written to kmsg; because we may not have Logd during // boot time. android::base::InitLogging(argv, &android::base::KernelLogger); -- cgit v1.2.3 From c35c1b079756e239ac4812ea6c4695aa6f710729 Mon Sep 17 00:00:00 2001 From: katao Date: Fri, 8 Dec 2017 11:02:43 +0800 Subject: recovery: Porting screensave mode for new platform. On new board platform the brightness path of sys/class/leds/lcd-backlight is deprecated,instead of /sys/class/backlight/panel0-backlight/. Test: reboot into recovery on sdm845. Change-Id: Idf0027ab888f9f982a8eef7de230ce3635e7c300 Signed-off-by: katao --- ui.cpp | 23 +++++++++++++++++------ ui.h | 2 ++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ui.cpp b/ui.cpp index baf6d1080..3c9ded735 100644 --- a/ui.cpp +++ b/ui.cpp @@ -48,10 +48,16 @@ static constexpr int UI_WAIT_KEY_TIMEOUT_SEC = 120; static constexpr const char* BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/brightness"; static constexpr const char* MAX_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/max_brightness"; +static constexpr const char* BRIGHTNESS_FILE_SDM = + "/sys/class/backlight/panel0-backlight/brightness"; +static constexpr const char* MAX_BRIGHTNESS_FILE_SDM = + "/sys/class/backlight/panel0-backlight/max_brightness"; RecoveryUI::RecoveryUI() : brightness_normal_(50), brightness_dimmed_(25), + brightness_file_(BRIGHTNESS_FILE), + max_brightness_file_(MAX_BRIGHTNESS_FILE), touch_screen_allowed_(false), kTouchLowThreshold(RECOVERY_UI_TOUCH_LOW_THRESHOLD), kTouchHighThreshold(RECOVERY_UI_TOUCH_HIGH_THRESHOLD), @@ -101,12 +107,17 @@ bool RecoveryUI::InitScreensaver() { if (brightness_normal_ == 0 || brightness_dimmed_ > brightness_normal_) { return false; } - + if (access(brightness_file_.c_str(), R_OK | W_OK)) { + brightness_file_ = BRIGHTNESS_FILE_SDM; + } + if (access(max_brightness_file_.c_str(), R_OK)) { + max_brightness_file_ = MAX_BRIGHTNESS_FILE_SDM; + } // Set the initial brightness level based on the max brightness. Note that reading the initial // value from BRIGHTNESS_FILE doesn't give the actual brightness value (bullhead, sailfish), so // we don't have a good way to query the default value. std::string content; - if (!android::base::ReadFileToString(MAX_BRIGHTNESS_FILE, &content)) { + if (!android::base::ReadFileToString(max_brightness_file_, &content)) { PLOG(WARNING) << "Failed to read max brightness"; return false; } @@ -120,7 +131,7 @@ bool RecoveryUI::InitScreensaver() { brightness_normal_value_ = max_value * brightness_normal_ / 100.0; brightness_dimmed_value_ = max_value * brightness_dimmed_ / 100.0; if (!android::base::WriteStringToFile(std::to_string(brightness_normal_value_), - BRIGHTNESS_FILE)) { + brightness_file_)) { PLOG(WARNING) << "Failed to set brightness"; return false; } @@ -430,13 +441,13 @@ int RecoveryUI::WaitKey() { // Lower the brightness level: NORMAL -> DIMMED; DIMMED -> OFF. if (screensaver_state_ == ScreensaverState::NORMAL) { if (android::base::WriteStringToFile(std::to_string(brightness_dimmed_value_), - BRIGHTNESS_FILE)) { + brightness_file_)) { LOG(INFO) << "Brightness: " << brightness_dimmed_value_ << " (" << brightness_dimmed_ << "%)"; screensaver_state_ = ScreensaverState::DIMMED; } } else if (screensaver_state_ == ScreensaverState::DIMMED) { - if (android::base::WriteStringToFile("0", BRIGHTNESS_FILE)) { + if (android::base::WriteStringToFile("0", brightness_file_)) { LOG(INFO) << "Brightness: 0 (off)"; screensaver_state_ = ScreensaverState::OFF; } @@ -451,7 +462,7 @@ int RecoveryUI::WaitKey() { // Reset the brightness to normal. if (android::base::WriteStringToFile(std::to_string(brightness_normal_value_), - BRIGHTNESS_FILE)) { + brightness_file_)) { screensaver_state_ = ScreensaverState::NORMAL; LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_ << "%)"; diff --git a/ui.h b/ui.h index 4086023af..4c54d6915 100644 --- a/ui.h +++ b/ui.h @@ -148,6 +148,8 @@ class RecoveryUI { // be configured via subclassing. Setting brightness_normal_ to 0 to disable screensaver. unsigned int brightness_normal_; unsigned int brightness_dimmed_; + std::string brightness_file_; + std::string max_brightness_file_; // Whether we should listen for touch inputs (default: false). bool touch_screen_allowed_; -- cgit v1.2.3 From 1d65c95fe8284c7b97823c4a719505d773fcea55 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 20 Dec 2017 12:36:31 -0800 Subject: StartsWith allows a std::string prefix now. Bug: N/A Test: builds Change-Id: I5183ec8133f5dc9a81a438223c6d3d2ea11ef0ec --- minui/resources.cpp | 2 +- tests/manual/recovery_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/minui/resources.cpp b/minui/resources.cpp index 837f5ebca..52ab60b1b 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -397,7 +397,7 @@ bool matches_locale(const std::string& prefix, const std::string& locale) { // match the locale string without the {script} section. // For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale // == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN". - if (android::base::StartsWith(locale, prefix.c_str())) { + if (android::base::StartsWith(locale, prefix)) { return true; } diff --git a/tests/manual/recovery_test.cpp b/tests/manual/recovery_test.cpp index 92c6ef2d4..64e3b59e6 100644 --- a/tests/manual/recovery_test.cpp +++ b/tests/manual/recovery_test.cpp @@ -209,7 +209,7 @@ TEST_P(ResourceTest, ValidateLocale) { ASSERT_GT(height, y + 1 + h) << "Locale: " << kLocale << " is not found in the file."; char* loc = reinterpret_cast(&row[5]); if (matches_locale(loc, kLocale.c_str())) { - EXPECT_TRUE(android::base::StartsWith(loc, kLocale.c_str())); + EXPECT_TRUE(android::base::StartsWith(loc, kLocale)); break; } else { for (int i = 0; i < h; ++i, ++y) { -- cgit v1.2.3 From bd84bd438aa8bee6ed975af962a4d8abe0a5ce57 Mon Sep 17 00:00:00 2001 From: Hridya Valsaraju Date: Wed, 10 Jan 2018 16:18:42 -0800 Subject: Statically link with libhealthstoragedefault This is to add empty implementations of Health HAL methods to report storage info. Bug: 68388678 Test: vts-tradefed run vts -m VtsHalHealthV2_0 Change-Id: I4f093903f485519e16f39384f5c96fcd031438ba --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index 54f405466..199cee7ec 100644 --- a/Android.mk +++ b/Android.mk @@ -153,6 +153,7 @@ LOCAL_STATIC_LIBRARIES := \ android.hardware.health@2.0 \ android.hardware.health@1.0 \ android.hardware.health@1.0-convert \ + libhealthstoragedefault \ libhidltransport \ libhidlbase \ libhwbinder \ -- cgit v1.2.3 From 7191bf0492168a2b488eb3ce52129def996b40ea Mon Sep 17 00:00:00 2001 From: Sen Jiang Date: Mon, 22 Jan 2018 15:01:48 -0800 Subject: Add update_channel field to bootloader_message_ab. The update_channel field is used to store the Omaha update channel if update_engine is compiled with Omaha support. We need it to be in misc to persist through factory reset. Bug: 72332119 Test: mma Change-Id: Ied4fecc6e78cc69d33a36ba4d101d675100f9d82 --- bootloader_message/include/bootloader_message/bootloader_message.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 798f3bb8c..95c19ae54 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -103,13 +103,17 @@ static_assert(sizeof(struct bootloader_message) == 2048, * implementations are free to use all 32 bytes and may store private * data past the first NUL-byte in this field. It is encouraged, but * not mandatory, to use 'struct bootloader_control' described below. + * + * The update_channel field is used to store the Omaha update channel + * if update_engine is compiled with Omaha support. */ struct bootloader_message_ab { struct bootloader_message message; char slot_suffix[32]; + char update_channel[128]; // Round up the entire struct to 4096-byte. - char reserved[2016]; + char reserved[1888]; }; /** -- cgit v1.2.3 From 5ad802839d84f7bcdfbb8aa5c48cb56b303ce5f0 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Sun, 28 Jan 2018 15:37:48 -0800 Subject: Avoid overwrite of the error message in AbortFn The AbortFn() used to overwrite the error message, hiding the real failure reported in ErrorAbort(). And we will miss the failure in the script patterns like 'blockimageupdate() || abort()' We will ensure there's one line break at the end of ErrorAbort's error message; and append to the existing error message when calling abort(). Test: Message from ErrorAbort shows up in the log Change-Id: I3aebd06629c5129330250c7fe5e8cdead2ae85bc --- edify/expr.cpp | 19 +++++++++++-------- updater/blockimg.cpp | 18 +++++++++--------- updater/install.cpp | 12 ++++++------ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/edify/expr.cpp b/edify/expr.cpp index 1b8623f03..6823b7339 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -114,9 +114,9 @@ Value* IfElseFn(const char* name, State* state, const std::vector>& argv) { std::string msg; if (!argv.empty() && Evaluate(state, argv[0], &msg)) { - state->errmsg = msg; + state->errmsg += msg; } else { - state->errmsg = "called abort()"; + state->errmsg += "called abort()"; } return nullptr; } @@ -410,12 +410,15 @@ Value* ErrorAbort(State* state, const char* format, ...) { } Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) { - va_list ap; - va_start(ap, format); - android::base::StringAppendV(&state->errmsg, format, ap); - va_end(ap); - state->cause_code = cause_code; - return nullptr; + std::string err_message; + va_list ap; + va_start(ap, format); + android::base::StringAppendV(&err_message, format, ap); + va_end(ap); + // Ensure that there's exactly one line break at the end of the error message. + state->errmsg = android::base::Trim(err_message) + "\n"; + state->cause_code = cause_code; + return nullptr; } State::State(const std::string& script, void* cookie) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 08f9930ea..0e90e94a1 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -817,7 +817,7 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& blockd size_t max_stash_size = maxblocks * BLOCKSIZE; if (res == -1 && errno != ENOENT) { - ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s\n", dirname.c_str(), + ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s", dirname.c_str(), strerror(errno)); return -1; } else if (res != 0) { @@ -825,19 +825,19 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& blockd res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE); if (res != 0) { - ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n", dirname.c_str(), + ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s", dirname.c_str(), strerror(errno)); return -1; } if (chown(dirname.c_str(), AID_SYSTEM, AID_SYSTEM) != 0) { // system user - ErrorAbort(state, kStashCreationFailure, "chown \"%s\" failed: %s\n", dirname.c_str(), + ErrorAbort(state, kStashCreationFailure, "chown \"%s\" failed: %s", dirname.c_str(), strerror(errno)); return -1; } if (CacheSizeCheck(max_stash_size) != 0) { - ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)\n", + ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)", max_stash_size); return -1; } @@ -869,7 +869,7 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& blockd if (max_stash_size > existing) { size_t needed = max_stash_size - existing; if (CacheSizeCheck(needed) != 0) { - ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu more needed)\n", + ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu more needed)", needed); return -1; } @@ -1517,7 +1517,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, std::vector lines = android::base::Split(transfer_list_value->data, "\n"); if (lines.size() < 2) { - ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zd]\n", + ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zd]", lines.size()); return StringValue(""); } @@ -1533,7 +1533,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, // Second line in transfer list is the total number of blocks we expect to write. size_t total_blocks; if (!android::base::ParseUint(lines[1], &total_blocks)) { - ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]\n", lines[1].c_str()); + ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]", lines[1].c_str()); return StringValue(""); } @@ -1543,7 +1543,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, size_t start = 2; if (lines.size() < 4) { - ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]\n", + ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]", lines.size()); return StringValue(""); } @@ -1554,7 +1554,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, // Fourth line is the maximum number of blocks that will be stashed simultaneously size_t stash_max_blocks; if (!android::base::ParseUint(lines[3], &stash_max_blocks)) { - ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]\n", + ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]", lines[3].c_str()); return StringValue(""); } diff --git a/updater/install.cpp b/updater/install.cpp index b83d30ff3..eef252e30 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -268,7 +268,7 @@ Value* FormatFn(const char* name, State* state, const std::vector Date: Tue, 5 Dec 2017 11:04:17 -0800 Subject: Log the last command to cache When performing an update, save the index and cmdline of the current command into the last command file if this command writes to the stash either explicitly of implicitly. This mitigates the overhead to update the last command file for every command. I ran a simple test on angler and the time to update 1000 times is ~2.3 seconds. Upon resuming an update, read the saved index first; then 1. In verification mode, check if all commands before the saved index have already produced the expected target blocks. If not, delete the last command file so that we will later resume the update from the start of the transfer list. 2. In update mode, skip all commands before the saved index. Therefore, we can avoid deleting stashes with duplicate id unintentionally; and also speed up the update. If an update succeeds or is unresumable, delete the last command file. Bug: 69858743 Test: Unittest passed, apply a failed update with invalid cmd on angler and check the last_command content, apply a failed update with invalid source hash and last_command is deleted. Change-Id: Ib60ba1e3c6d111d9f33097759b17dbcef97a37bf --- tests/component/updater_test.cpp | 217 +++++++++++++++++++++++++++++++++++++ updater/blockimg.cpp | 181 +++++++++++++++++++++++++++++-- updater/include/updater/blockimg.h | 3 + 3 files changed, 392 insertions(+), 9 deletions(-) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index d9d01d427..448fe4935 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -707,3 +707,220 @@ TEST_F(UpdaterTest, brotli_new_data) { ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } + +TEST_F(UpdaterTest, last_command_update) { + TemporaryFile temp_file; + last_command_file = temp_file.path; + + std::string block1 = std::string(4096, '1'); + std::string block2 = std::string(4096, '2'); + std::string block3 = std::string(4096, '3'); + std::string block1_hash = get_sha1(block1); + std::string block2_hash = get_sha1(block2); + std::string block3_hash = get_sha1(block3); + + // Compose the transfer list to fail the first update. + std::vector transfer_list_fail = { + "4", + "2", + "0", + "2", + "stash " + block1_hash + " 2,0,1", + "move " + block1_hash + " 2,1,2 1 2,0,1", + "stash " + block3_hash + " 2,2,3", + "fail", + }; + + // Mimic a resumed update with the same transfer commands. + std::vector transfer_list_continue = { + "4", + "2", + "0", + "2", + "stash " + block1_hash + " 2,0,1", + "move " + block1_hash + " 2,1,2 1 2,0,1", + "stash " + block3_hash + " 2,2,3", + "move " + block1_hash + " 2,2,3 1 2,0,1", + }; + + std::unordered_map entries = { + { "new_data", "" }, + { "patch_data", "" }, + { "transfer_list_fail", android::base::Join(transfer_list_fail, '\n') }, + { "transfer_list_continue", android::base::Join(transfer_list_continue, '\n') }, + }; + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + std::string src_content = block1 + block2 + block3; + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + std::string script = + "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list_fail"), "new_data", "patch_data"))"; + expect("", script.c_str(), kNoCause, &updater_info); + + // Expect last_command to contain the last stash command. + std::string last_command_content; + ASSERT_TRUE(android::base::ReadFileToString(last_command_file.c_str(), &last_command_content)); + EXPECT_EQ("2\nstash " + block3_hash + " 2,2,3", last_command_content); + std::string updated_contents; + ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_contents)); + ASSERT_EQ(block1 + block1 + block3, updated_contents); + + // Resume the update, expect the first 'move' to be skipped but the second 'move' to be executed. + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + std::string script_second_update = + "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list_continue"), "new_data", "patch_data"))"; + expect("t", script_second_update.c_str(), kNoCause, &updater_info); + ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_contents)); + ASSERT_EQ(block1 + block2 + block1, updated_contents); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, last_command_update_unresumable) { + TemporaryFile temp_file; + last_command_file = temp_file.path; + + std::string block1 = std::string(4096, '1'); + std::string block2 = std::string(4096, '2'); + std::string block1_hash = get_sha1(block1); + std::string block2_hash = get_sha1(block2); + + // Construct an unresumable update with source blocks mismatch. + std::vector transfer_list_unresumable = { + "4", "2", "0", "2", "stash " + block1_hash + " 2,0,1", "move " + block2_hash + " 2,1,2 1 2,0,1", + }; + + std::unordered_map entries = { + { "new_data", "" }, + { "patch_data", "" }, + { "transfer_list_unresumable", android::base::Join(transfer_list_unresumable, '\n') }, + }; + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + // Set up the last_command_file + ASSERT_TRUE( + android::base::WriteStringToFile("0\nstash " + block1_hash + " 2,0,1", last_command_file)); + + // The last_command_file will be deleted if the update encounters an unresumable failure + // later. + std::string src_content = block1 + block1; + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + std::string script = + "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list_unresumable"), "new_data", "patch_data"))"; + expect("", script.c_str(), kNoCause, &updater_info); + ASSERT_EQ(-1, access(last_command_file.c_str(), R_OK)); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, last_command_verify) { + TemporaryFile temp_file; + last_command_file = temp_file.path; + + std::string block1 = std::string(4096, '1'); + std::string block2 = std::string(4096, '2'); + std::string block3 = std::string(4096, '3'); + std::string block1_hash = get_sha1(block1); + std::string block2_hash = get_sha1(block2); + std::string block3_hash = get_sha1(block3); + + std::vector transfer_list_verify = { + "4", + "2", + "0", + "2", + "stash " + block1_hash + " 2,0,1", + "move " + block1_hash + " 2,0,1 1 2,0,1", + "move " + block1_hash + " 2,1,2 1 2,0,1", + "stash " + block3_hash + " 2,2,3", + }; + + std::unordered_map entries = { + { "new_data", "" }, + { "patch_data", "" }, + { "transfer_list_verify", android::base::Join(transfer_list_verify, '\n') }, + }; + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + std::string src_content = block1 + block1 + block3; + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + + ASSERT_TRUE( + android::base::WriteStringToFile("2\nstash " + block3_hash + " 2,2,3", last_command_file)); + + // Expect the verification to succeed and the last_command_file is intact. + std::string script_verify = + "block_image_verify(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list_verify"), "new_data","patch_data"))"; + expect("t", script_verify.c_str(), kNoCause, &updater_info); + + std::string last_command_content; + ASSERT_TRUE(android::base::ReadFileToString(last_command_file.c_str(), &last_command_content)); + EXPECT_EQ("2\nstash " + block3_hash + " 2,2,3", last_command_content); + + // Expect the verification to succeed but last_command_file to be deleted; because the target + // blocks don't have the expected contents for the second move command. + src_content = block1 + block2 + block3; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + expect("t", script_verify.c_str(), kNoCause, &updater_info); + ASSERT_EQ(-1, access(last_command_file.c_str(), R_OK)); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 0e90e94a1..feb2aeb27 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -34,11 +34,13 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -67,10 +69,96 @@ static constexpr const char* STASH_DIRECTORY_BASE = "/cache/recovery"; static constexpr mode_t STASH_DIRECTORY_MODE = 0700; static constexpr mode_t STASH_FILE_MODE = 0600; +std::string last_command_file = "/cache/recovery/last_command"; + static CauseCode failure_type = kNoCause; static bool is_retry = false; static std::unordered_map stash_map; +static void DeleteLastCommandFile() { + if (unlink(last_command_file.c_str()) == -1 && errno != ENOENT) { + PLOG(ERROR) << "Failed to unlink: " << last_command_file; + } +} + +// Parse the last command index of the last update and save the result to |last_command_index|. +// Return true if we successfully read the index. +static bool ParseLastCommandFile(int* last_command_index) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(last_command_file.c_str(), O_RDONLY))); + if (fd == -1) { + if (errno != ENOENT) { + PLOG(ERROR) << "Failed to open " << last_command_file; + return false; + } + + LOG(INFO) << last_command_file << " doesn't exist."; + return false; + } + + // Now that the last_command file exists, parse the last command index of previous update. + std::string content; + if (!android::base::ReadFdToString(fd.get(), &content)) { + LOG(ERROR) << "Failed to read: " << last_command_file; + return false; + } + + std::vector lines = android::base::Split(android::base::Trim(content), "\n"); + if (lines.size() != 2) { + LOG(ERROR) << "Unexpected line counts in last command file: " << content; + return false; + } + + if (!android::base::ParseInt(lines[0], last_command_index)) { + LOG(ERROR) << "Failed to parse integer in: " << lines[0]; + return false; + } + + return true; +} + +// Update the last command index in the last_command_file if the current command writes to the +// stash either explicitly or implicitly. +static bool UpdateLastCommandIndex(int command_index, const std::string& command_string) { + std::string last_command_tmp = last_command_file + ".tmp"; + std::string content = std::to_string(command_index) + "\n" + command_string; + android::base::unique_fd wfd( + TEMP_FAILURE_RETRY(open(last_command_tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0660))); + if (wfd == -1 || !android::base::WriteStringToFd(content, wfd)) { + PLOG(ERROR) << "Failed to update last command"; + return false; + } + + if (fsync(wfd) == -1) { + PLOG(ERROR) << "Failed to fsync " << last_command_tmp; + return false; + } + + if (chown(last_command_tmp.c_str(), AID_SYSTEM, AID_SYSTEM) == -1) { + PLOG(ERROR) << "Failed to change owner for " << last_command_tmp; + return false; + } + + if (rename(last_command_tmp.c_str(), last_command_file.c_str()) == -1) { + PLOG(ERROR) << "Failed to rename" << last_command_tmp; + return false; + } + + std::string last_command_dir = android::base::Dirname(last_command_file); + android::base::unique_fd dfd( + TEMP_FAILURE_RETRY(ota_open(last_command_dir.c_str(), O_RDONLY | O_DIRECTORY))); + if (dfd == -1) { + PLOG(ERROR) << "Failed to open " << last_command_dir; + return false; + } + + if (fsync(dfd) == -1) { + PLOG(ERROR) << "Failed to fsync " << last_command_dir; + return false; + } + + return true; +} + static int read_all(int fd, uint8_t* data, size_t size) { size_t so_far = 0; while (so_far < size) { @@ -439,6 +527,7 @@ static int WriteBlocks(const RangeSet& tgt, const std::vector& buffer, struct CommandParameters { std::vector tokens; size_t cpos; + int cmdindex; const char* cmdname; const char* cmdline; std::string freestash; @@ -455,6 +544,7 @@ struct CommandParameters { pthread_t thread; std::vector buffer; uint8_t* patch_start; + bool target_verified; // The target blocks have expected contents already. }; // Print the hash in hex for corrupted source blocks (excluding the stashed blocks which is @@ -1072,6 +1162,10 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* return -1; } + if (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) { + LOG(WARNING) << "Failed to update the last command file."; + } + params.stashed += *src_blocks; // Can be deleted when the write has completed. if (!stash_exists) { @@ -1112,8 +1206,11 @@ static int PerformCommandMove(CommandParameters& params) { if (status == 0) { params.foundwrites = true; - } else if (params.foundwrites) { - LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; + } else { + params.target_verified = true; + if (params.foundwrites) { + LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; + } } if (params.canwrite) { @@ -1177,8 +1274,15 @@ static int PerformCommandStash(CommandParameters& params) { } LOG(INFO) << "stashing " << blocks << " blocks to " << id; - params.stashed += blocks; - return WriteStash(params.stashbase, id, blocks, params.buffer, false, nullptr); + int result = WriteStash(params.stashbase, id, blocks, params.buffer, false, nullptr); + if (result == 0) { + if (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) { + LOG(WARNING) << "Failed to update the last command file."; + } + + params.stashed += blocks; + } + return result; } static int PerformCommandFree(CommandParameters& params) { @@ -1306,8 +1410,11 @@ static int PerformCommandDiff(CommandParameters& params) { if (status == 0) { params.foundwrites = true; - } else if (params.foundwrites) { - LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; + } else { + params.target_verified = true; + if (params.foundwrites) { + LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; + } } if (params.canwrite) { @@ -1566,6 +1673,23 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, params.createdstash = res; + // When performing an update, save the index and cmdline of the current command into + // the last_command_file if this command writes to the stash either explicitly of implicitly. + // Upon resuming an update, read the saved index first; then + // 1. In verification mode, check if the 'move' or 'diff' commands before the saved index has + // the expected target blocks already. If not, these commands cannot be skipped and we need + // to attempt to execute them again. Therefore, we will delete the last_command_file so that + // the update will resume from the start of the transfer list. + // 2. In update mode, skip all commands before the saved index. Therefore, we can avoid deleting + // stashes with duplicate id unintentionally (b/69858743); and also speed up the update. + // If an update succeeds or is unresumable, delete the last_command_file. + int saved_last_command_index; + if (!ParseLastCommandFile(&saved_last_command_index)) { + DeleteLastCommandFile(); + // We failed to parse the last command, set it explicitly to -1. + saved_last_command_index = -1; + } + start += 2; // Build a map of the available commands @@ -1581,14 +1705,20 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int rc = -1; // Subsequent lines are all individual transfer commands - for (auto it = lines.cbegin() + start; it != lines.cend(); it++) { - const std::string& line(*it); + for (size_t i = start; i < lines.size(); i++) { + const std::string& line = lines[i]; if (line.empty()) continue; params.tokens = android::base::Split(line, " "); params.cpos = 0; + if (i - start > std::numeric_limits::max()) { + params.cmdindex = -1; + } else { + params.cmdindex = i - start; + } params.cmdname = params.tokens[params.cpos++].c_str(); params.cmdline = line.c_str(); + params.target_verified = false; if (cmd_map.find(params.cmdname) == cmd_map.end()) { LOG(ERROR) << "unexpected command [" << params.cmdname << "]"; @@ -1597,11 +1727,38 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, const Command* cmd = cmd_map[params.cmdname]; - if (cmd->f != nullptr && cmd->f(params) == -1) { + if (cmd->f == nullptr) { + LOG(ERROR) << "failed to find the function for command [" << line << "]"; + goto pbiudone; + } + + // Skip all commands before the saved last command index when resuming an update. + if (params.canwrite && params.cmdindex != -1 && params.cmdindex <= saved_last_command_index) { + LOG(INFO) << "Skipping already executed command: " << params.cmdindex + << ", last executed command for previous update: " << saved_last_command_index; + continue; + } + + if (cmd->f(params) == -1) { LOG(ERROR) << "failed to execute command [" << line << "]"; goto pbiudone; } + // In verify mode, check if the commands before the saved last_command_index have been + // executed correctly. If some target blocks have unexpected contents, delete the last command + // file so that we will resume the update from the first command in the transfer list. + if (!params.canwrite && saved_last_command_index != -1 && params.cmdindex != -1 && + params.cmdindex <= saved_last_command_index) { + // TODO(xunchang) check that the cmdline of the saved index is correct. + std::string cmdname = std::string(params.cmdname); + if ((cmdname == "move" || cmdname == "bsdiff" || cmdname == "imgdiff") && + !params.target_verified) { + LOG(WARNING) << "Previously executed command " << saved_last_command_index << ": " + << params.cmdline << " doesn't produce expected target blocks."; + saved_last_command_index = -1; + DeleteLastCommandFile(); + } + } if (params.canwrite) { if (ota_fsync(params.fd) == -1) { failure_type = kFsyncFailure; @@ -1643,6 +1800,7 @@ pbiudone: // Delete stash only after successfully completing the update, as it may contain blocks needed // to complete the update later. DeleteStash(params.stashbase); + DeleteLastCommandFile(); } pthread_mutex_destroy(¶ms.nti.mu); @@ -1661,6 +1819,11 @@ pbiudone: BrotliDecoderDestroyInstance(params.nti.brotli_decoder_state); } + // Delete the last command file if the update cannot be resumed. + if (params.isunresumable) { + DeleteLastCommandFile(); + } + // Only delete the stash if the update cannot be resumed, or it's a verification run and we // created the stash. if (params.isunresumable || (!params.canwrite && params.createdstash)) { diff --git a/updater/include/updater/blockimg.h b/updater/include/updater/blockimg.h index 2f4ad3c04..2cc68ce9d 100644 --- a/updater/include/updater/blockimg.h +++ b/updater/include/updater/blockimg.h @@ -17,6 +17,9 @@ #ifndef _UPDATER_BLOCKIMG_H_ #define _UPDATER_BLOCKIMG_H_ +#include + +extern std::string last_command_file; void RegisterBlockImageFunctions(); #endif -- cgit v1.2.3 From c84a4ef0538ca2a8777efea2b09a413d18ed460b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 7 Feb 2018 09:15:36 -0800 Subject: Document instructions for using adb under recovery. Fixes: 72740736 Test: N/A Change-Id: Ifc96ed785fd80501bc6c276cb649c8cc1f05be0e --- README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/README.md b/README.md index 8e20b5a62..0aeadaeb4 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,94 @@ image under recovery. 1. `adb sync data` to make sure the test-dir has the images to test. 2. The test will automatically pickup and verify all `_text.png` files in the test dir. + +Using `adb` under recovery +-------------------------- + +When running recovery image from debuggable builds (i.e. `-eng` or `-userdebug` build variants, or +`ro.debuggable=1` in `/prop.default`), `adbd` service is enabled and started by default, which +allows `adb` communication. A device should be listed under `adb devices`, either in `recovery` or +`sideload` state. + + $ adb devices + List of devices attached + 1234567890abcdef recovery + +Although `/sbin/adbd` shares the same binary between normal boot and recovery images, only a subset +of `adb` commands are meaningful under recovery, such as `adb root`, `adb shell`, `adb push`, `adb +pull` etc. `adb shell` works only after manually mounting `/system` from recovery menu (assuming a +valid system image on device). + +## Troubleshooting + +### `adb devices` doesn't show the device. + + $ adb devices + List of devices attached + + * Ensure `adbd` is built and running. + +By default, `adbd` is always included into recovery image, as `/sbin/adbd`. `init` starts `adbd` +service automatically only in debuggable builds. This behavior is controlled by the recovery +specific `/init.rc`, whose source code is at `bootable/recovery/etc/init.rc`. + +The best way to confirm a running `adbd` is by checking the serial output, which shows a service +start log as below. + + [ 18.961986] c1 1 init: starting service 'adbd'... + + * Ensure USB gadget has been enabled. + +If `adbd` service has been started but device not shown under `adb devices`, use `lsusb(8)` (on +host) to check if the device is visible to the host. + +`bootable/recovery/etc/init.rc` disables Android USB gadget (via sysfs) as part of the `fs` action +trigger, and will only re-enable it in debuggable builds (the `on property` rule will always run +_after_ `on fs`). + + on fs + write /sys/class/android_usb/android0/enable 0 + + # Always start adbd on userdebug and eng builds + on property:ro.debuggable=1 + write /sys/class/android_usb/android0/enable 1 + start adbd + +If device is using [configfs](https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt), +check if configfs has been properly set up in init rc scripts. See the [example +configuration](https://android.googlesource.com/device/google/wahoo/+/master/init.recovery.hardware.rc) +for Pixel 2 devices. Note that the flag set via sysfs (i.e. the one above) is no-op when using +configfs. + +### `adb devices` shows the device, but in `unauthorized` state. + + $ adb devices + List of devices attached + 1234567890abcdef unauthorized + +recovery image doesn't honor the USB debugging toggle and the authorizations added under normal boot +(because such authorization data stays in /data, which recovery doesn't mount), nor does it support +authorizing a host device under recovery. We can use one of the following options instead. + + * **Option 1 (Recommended):** Authorize a host device with adb vendor keys. + +For debuggable builds, an RSA keypair can be used to authorize a host device that has the private +key. The public key, defined via `PRODUCT_ADB_KEYS`, will be copied to `/adb_keys`. When starting +the host-side `adbd`, make sure the filename (or the directory) of the matching private key has been +added to `$ADB_VENDOR_KEYS`. + + $ export ADB_VENDOR_KEYS=/path/to/adb/private/key + $ adb kill-server + $ adb devices + +`-user` builds filter out `PRODUCT_ADB_KEYS`, so no `/adb_keys` will be included there. + +Note that this mechanism applies to both of normal boot and recovery modes. + + * **Option 2:** Allow `adbd` to connect without authentication. + * `adbd` is compiled with `ALLOW_ADBD_NO_AUTH` (only on debuggable builds). + * `ro.adb.secure` has a value of `0`. + +Both of the two conditions need to be satisfied. Although `ro.adb.secure` is a runtime property, its +value is set at build time (written into `/prop.default`). It defaults to `1` on `-user` builds, and +`0` for other build variants. The value is overridable via `PRODUCT_DEFAULT_PROPERTY_OVERRIDES`. -- cgit v1.2.3 From 5419ad31e7dafae35ab1ac9f56e234fff0c2de50 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 2 Feb 2018 16:49:15 -0800 Subject: Reorder the functions in updater/install.cpp There is no logical change to the file; merely the function definition reorder and some comestic change to make the future review easier. Test: mma Change-Id: I7ffe952f8c78e840f10aa6bfad0c4b5a58e29896 --- updater/install.cpp | 483 ++++++++++++++++++++++++++-------------------------- 1 file changed, 244 insertions(+), 239 deletions(-) diff --git a/updater/install.cpp b/updater/install.cpp index eef252e30..2b6c20fe3 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -94,6 +94,244 @@ void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) { uiPrint(state, error_msg); } +// This is the updater side handler for ui_print() in edify script. Contents will be sent over to +// the recovery side for on-screen display. +Value* UIPrintFn(const char* name, State* state, const std::vector>& argv) { + std::vector args; + if (!ReadArgs(state, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); + } + + std::string buffer = android::base::Join(args, ""); + uiPrint(state, buffer); + return StringValue(buffer); +} + +// package_extract_file(package_file[, dest_file]) +// Extracts a single package_file from the update package and writes it to dest_file, +// overwriting existing files if necessary. Without the dest_file argument, returns the +// contents of the package file as a binary blob. +Value* PackageExtractFileFn(const char* name, State* state, + const std::vector>& argv) { + if (argv.size() < 1 || argv.size() > 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %zu", name, + argv.size()); + } + + if (argv.size() == 2) { + // The two-argument version extracts to a file. + + std::vector args; + if (!ReadArgs(state, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name, + argv.size()); + } + const std::string& zip_path = args[0]; + const std::string& dest_path = args[1]; + + ZipArchiveHandle za = static_cast(state->cookie)->package_zip; + ZipString zip_string_path(zip_path.c_str()); + ZipEntry entry; + if (FindEntry(za, zip_string_path, &entry) != 0) { + LOG(ERROR) << name << ": no " << zip_path << " in package"; + return StringValue(""); + } + + unique_fd fd(TEMP_FAILURE_RETRY( + ota_open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR))); + if (fd == -1) { + PLOG(ERROR) << name << ": can't open " << dest_path << " for write"; + return StringValue(""); + } + + bool success = true; + int32_t ret = ExtractEntryToFile(za, &entry, fd); + if (ret != 0) { + LOG(ERROR) << name << ": Failed to extract entry \"" << zip_path << "\" (" + << entry.uncompressed_length << " bytes) to \"" << dest_path + << "\": " << ErrorCodeString(ret); + success = false; + } + if (ota_fsync(fd) == -1) { + PLOG(ERROR) << "fsync of \"" << dest_path << "\" failed"; + success = false; + } + if (ota_close(fd) == -1) { + PLOG(ERROR) << "close of \"" << dest_path << "\" failed"; + success = false; + } + + return StringValue(success ? "t" : ""); + } else { + // The one-argument version returns the contents of the file as the result. + + std::vector args; + if (!ReadArgs(state, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name, + argv.size()); + } + const std::string& zip_path = args[0]; + + ZipArchiveHandle za = static_cast(state->cookie)->package_zip; + ZipString zip_string_path(zip_path.c_str()); + ZipEntry entry; + if (FindEntry(za, zip_string_path, &entry) != 0) { + return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name, + zip_path.c_str()); + } + + std::string buffer; + buffer.resize(entry.uncompressed_length); + + int32_t ret = + ExtractToMemory(za, &entry, reinterpret_cast(&buffer[0]), buffer.size()); + if (ret != 0) { + return ErrorAbort(state, kPackageExtractFileFailure, + "%s: Failed to extract entry \"%s\" (%zu bytes) to memory: %s", name, + zip_path.c_str(), buffer.size(), ErrorCodeString(ret)); + } + + return new Value(VAL_BLOB, buffer); + } +} + +// apply_patch(src_file, tgt_file, tgt_sha1, tgt_size, patch1_sha1, patch1_blob, [...]) +// Applies a binary patch to the src_file to produce the tgt_file. If the desired target is the +// same as the source, pass "-" for tgt_file. tgt_sha1 and tgt_size are the expected final SHA1 +// hash and size of the target file. The remaining arguments must come in pairs: a SHA1 hash (a +// 40-character hex string) and a blob. The blob is the patch to be applied when the source +// file's current contents have the given SHA1. +// +// The patching is done in a safe manner that guarantees the target file either has the desired +// SHA1 hash and size, or it is untouched -- it will not be left in an unrecoverable intermediate +// state. If the process is interrupted during patching, the target file may be in an intermediate +// state; a copy exists in the cache partition so restarting the update can successfully update +// the file. +Value* ApplyPatchFn(const char* name, State* state, + const std::vector>& argv) { + if (argv.size() < 6 || (argv.size() % 2) == 1) { + return ErrorAbort(state, kArgsParsingFailure, + "%s(): expected at least 6 args and an " + "even number, got %zu", + name, argv.size()); + } + + std::vector args; + if (!ReadArgs(state, argv, &args, 0, 4)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& source_filename = args[0]; + const std::string& target_filename = args[1]; + const std::string& target_sha1 = args[2]; + const std::string& target_size_str = args[3]; + + size_t target_size; + if (!android::base::ParseUint(target_size_str.c_str(), &target_size)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", name, + target_size_str.c_str()); + } + + int patchcount = (argv.size() - 4) / 2; + std::vector> arg_values; + if (!ReadValueArgs(state, argv, &arg_values, 4, argv.size() - 4)) { + return nullptr; + } + + for (int i = 0; i < patchcount; ++i) { + if (arg_values[i * 2]->type != VAL_STRING) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, i * 2); + } + if (arg_values[i * 2 + 1]->type != VAL_BLOB) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name, i * 2 + 1); + } + } + + std::vector patch_sha_str; + std::vector> patches; + for (int i = 0; i < patchcount; ++i) { + patch_sha_str.push_back(arg_values[i * 2]->data); + patches.push_back(std::move(arg_values[i * 2 + 1])); + } + + int result = applypatch(source_filename.c_str(), target_filename.c_str(), target_sha1.c_str(), + target_size, patch_sha_str, patches, nullptr); + + return StringValue(result == 0 ? "t" : ""); +} + +// apply_patch_check(filename, [sha1, ...]) +// Returns true if the contents of filename or the temporary copy in the cache partition (if +// present) have a SHA-1 checksum equal to one of the given sha1 values. sha1 values are +// specified as 40 hex digits. This function differs from sha1_check(read_file(filename), +// sha1 [, ...]) in that it knows to check the cache partition copy, so apply_patch_check() will +// succeed even if the file was corrupted by an interrupted apply_patch() update. +Value* ApplyPatchCheckFn(const char* name, State* state, + const std::vector>& argv) { + if (argv.size() < 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %zu", name, + argv.size()); + } + + std::vector args; + if (!ReadArgs(state, argv, &args, 0, 1)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + + std::vector sha1s; + if (argv.size() > 1 && !ReadArgs(state, argv, &sha1s, 1, argv.size() - 1)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + int result = applypatch_check(filename.c_str(), sha1s); + + return StringValue(result == 0 ? "t" : ""); +} + +// sha1_check(data) +// to return the sha1 of the data (given in the format returned by +// read_file). +// +// sha1_check(data, sha1_hex, [sha1_hex, ...]) +// returns the sha1 of the file if it matches any of the hex +// strings passed, or "" if it does not equal any of them. +// +Value* Sha1CheckFn(const char* name, State* state, const std::vector>& argv) { + if (argv.size() < 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); + } + + std::vector> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } + + if (args[0]->type == VAL_INVALID) { + return StringValue(""); + } + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast(args[0]->data.c_str()), args[0]->data.size(), digest); + + if (argv.size() == 1) { + return StringValue(print_sha1(digest)); + } + + for (size_t i = 1; i < argv.size(); ++i) { + uint8_t arg_digest[SHA_DIGEST_LENGTH]; + if (args[i]->type != VAL_STRING) { + LOG(ERROR) << name << "(): arg " << i << " is not a string; skipping"; + } else if (ParseSha1(args[i]->data.c_str(), arg_digest) != 0) { + // Warn about bad args and skip them. + LOG(ERROR) << name << "(): error parsing \"" << args[i]->data << "\" as sha-1; skipping"; + } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) { + // Found a match. + return args[i].release(); + } + } + + // Didn't match any of the hex strings; return false. + return StringValue(""); +} + // mount(fs_type, partition_type, location, mount_point) // mount(fs_type, partition_type, location, mount_point, mount_options) @@ -367,7 +605,8 @@ Value* ShowProgressFn(const char* name, State* state, return StringValue(frac_str); } -Value* SetProgressFn(const char* name, State* state, const std::vector>& argv) { +Value* SetProgressFn(const char* name, State* state, + const std::vector>& argv) { if (argv.size() != 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size()); } @@ -390,93 +629,6 @@ Value* SetProgressFn(const char* name, State* state, const std::vector>& argv) { - if (argv.size() < 1 || argv.size() > 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %zu", name, - argv.size()); - } - - if (argv.size() == 2) { - // The two-argument version extracts to a file. - - std::vector args; - if (!ReadArgs(state, argv, &args)) { - return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name, - argv.size()); - } - const std::string& zip_path = args[0]; - const std::string& dest_path = args[1]; - - ZipArchiveHandle za = static_cast(state->cookie)->package_zip; - ZipString zip_string_path(zip_path.c_str()); - ZipEntry entry; - if (FindEntry(za, zip_string_path, &entry) != 0) { - LOG(ERROR) << name << ": no " << zip_path << " in package"; - return StringValue(""); - } - - unique_fd fd(TEMP_FAILURE_RETRY( - ota_open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR))); - if (fd == -1) { - PLOG(ERROR) << name << ": can't open " << dest_path << " for write"; - return StringValue(""); - } - - bool success = true; - int32_t ret = ExtractEntryToFile(za, &entry, fd); - if (ret != 0) { - LOG(ERROR) << name << ": Failed to extract entry \"" << zip_path << "\" (" - << entry.uncompressed_length << " bytes) to \"" << dest_path - << "\": " << ErrorCodeString(ret); - success = false; - } - if (ota_fsync(fd) == -1) { - PLOG(ERROR) << "fsync of \"" << dest_path << "\" failed"; - success = false; - } - if (ota_close(fd) == -1) { - PLOG(ERROR) << "close of \"" << dest_path << "\" failed"; - success = false; - } - - return StringValue(success ? "t" : ""); - } else { - // The one-argument version returns the contents of the file as the result. - - std::vector args; - if (!ReadArgs(state, argv, &args)) { - return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name, - argv.size()); - } - const std::string& zip_path = args[0]; - - ZipArchiveHandle za = static_cast(state->cookie)->package_zip; - ZipString zip_string_path(zip_path.c_str()); - ZipEntry entry; - if (FindEntry(za, zip_string_path, &entry) != 0) { - return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name, - zip_path.c_str()); - } - - std::string buffer; - buffer.resize(entry.uncompressed_length); - - int32_t ret = ExtractToMemory(za, &entry, reinterpret_cast(&buffer[0]), buffer.size()); - if (ret != 0) { - return ErrorAbort(state, kPackageExtractFileFailure, - "%s: Failed to extract entry \"%s\" (%zu bytes) to memory: %s", name, - zip_path.c_str(), buffer.size(), ErrorCodeString(ret)); - } - - return new Value(VAL_BLOB, buffer); - } -} - Value* GetPropFn(const char* name, State* state, const std::vector>& argv) { if (argv.size() != 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size()); @@ -495,7 +647,8 @@ Value* GetPropFn(const char* name, State* state, const std::vector>& argv) { +Value* FileGetPropFn(const char* name, State* state, + const std::vector>& argv) { if (argv.size() != 2) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name, argv.size()); @@ -561,7 +714,8 @@ Value* FileGetPropFn(const char* name, State* state, const std::vector>& argv) { +Value* ApplyPatchSpaceFn(const char* name, State* state, + const std::vector>& argv) { if (argv.size() != 1) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 args, got %zu", name, argv.size()); @@ -585,110 +739,6 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, const std::vector>& argv) { - if (argv.size() < 6 || (argv.size() % 2) == 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 6 args and an " - "even number, got %zu", name, argv.size()); - } - - std::vector args; - if (!ReadArgs(state, argv, &args, 0, 4)) { - return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); - } - const std::string& source_filename = args[0]; - const std::string& target_filename = args[1]; - const std::string& target_sha1 = args[2]; - const std::string& target_size_str = args[3]; - - size_t target_size; - if (!android::base::ParseUint(target_size_str.c_str(), &target_size)) { - return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", - name, target_size_str.c_str()); - } - - int patchcount = (argv.size()-4) / 2; - std::vector> arg_values; - if (!ReadValueArgs(state, argv, &arg_values, 4, argv.size() - 4)) { - return nullptr; - } - - for (int i = 0; i < patchcount; ++i) { - if (arg_values[i * 2]->type != VAL_STRING) { - return ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, - i * 2); - } - if (arg_values[i * 2 + 1]->type != VAL_BLOB) { - return ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name, - i * 2 + 1); - } - } - - std::vector patch_sha_str; - std::vector> patches; - for (int i = 0; i < patchcount; ++i) { - patch_sha_str.push_back(arg_values[i * 2]->data); - patches.push_back(std::move(arg_values[i * 2 + 1])); - } - - int result = applypatch(source_filename.c_str(), target_filename.c_str(), - target_sha1.c_str(), target_size, - patch_sha_str, patches, nullptr); - - return StringValue(result == 0 ? "t" : ""); -} - -// apply_patch_check(filename, [sha1, ...]) -// Returns true if the contents of filename or the temporary copy in the cache partition (if -// present) have a SHA-1 checksum equal to one of the given sha1 values. sha1 values are -// specified as 40 hex digits. This function differs from sha1_check(read_file(filename), -// sha1 [, ...]) in that it knows to check the cache partition copy, so apply_patch_check() will -// succeed even if the file was corrupted by an interrupted apply_patch() update. -Value* ApplyPatchCheckFn(const char* name, State* state, const std::vector>& argv) { - if (argv.size() < 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %zu", name, - argv.size()); - } - - std::vector args; - if (!ReadArgs(state, argv, &args, 0, 1)) { - return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); - } - const std::string& filename = args[0]; - - std::vector sha1s; - if (argv.size() > 1 && !ReadArgs(state, argv, &sha1s, 1, argv.size() - 1)) { - return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); - } - int result = applypatch_check(filename.c_str(), sha1s); - - return StringValue(result == 0 ? "t" : ""); -} - -// This is the updater side handler for ui_print() in edify script. Contents will be sent over to -// the recovery side for on-screen display. -Value* UIPrintFn(const char* name, State* state, const std::vector>& argv) { - std::vector args; - if (!ReadArgs(state, argv, &args)) { - return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); - } - - std::string buffer = android::base::Join(args, ""); - uiPrint(state, buffer); - return StringValue(buffer); -} - Value* WipeCacheFn(const char* name, State* state, const std::vector>& argv) { if (!argv.empty()) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name, @@ -736,51 +786,6 @@ Value* RunProgramFn(const char* name, State* state, const std::vector>& argv) { - if (argv.size() < 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); - } - - std::vector> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } - - if (args[0]->type == VAL_INVALID) { - return StringValue(""); - } - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast(args[0]->data.c_str()), args[0]->data.size(), digest); - - if (argv.size() == 1) { - return StringValue(print_sha1(digest)); - } - - for (size_t i = 1; i < argv.size(); ++i) { - uint8_t arg_digest[SHA_DIGEST_LENGTH]; - if (args[i]->type != VAL_STRING) { - LOG(ERROR) << name << "(): arg " << i << " is not a string; skipping"; - } else if (ParseSha1(args[i]->data.c_str(), arg_digest) != 0) { - // Warn about bad args and skip them. - LOG(ERROR) << name << "(): error parsing \"" << args[i]->data << "\" as sha-1; skipping"; - } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) { - // Found a match. - return args[i].release(); - } - } - - // Didn't match any of the hex strings; return false. - return StringValue(""); -} - // Read a local file and return its contents (the Value* returned // is actually a FileContents*). Value* ReadFileFn(const char* name, State* state, const std::vector>& argv) { -- cgit v1.2.3 From e40c80d021d667c1400b7a5d1f6d623b8450a47c Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Sat, 3 Feb 2018 17:20:56 -0800 Subject: Skip the cache size check on host When running the update simulation, we choose to skip the cache size check for now due to the lack of "/cache" on host. And in later cls we can implement a cache size estimator to make the check more constrained. Also build the host version of support libraries. Test: unit test pass Change-Id: I3ed93c857fd02f7b62f5baba9130f75c3236e717 --- applypatch/Android.bp | 2 ++ applypatch/applypatch.cpp | 5 ++--- applypatch/freecache.cpp | 6 ++++++ otafault/Android.bp | 2 ++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/applypatch/Android.bp b/applypatch/Android.bp index b37614072..d3efa152b 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -30,6 +30,8 @@ cc_defaults { cc_library_static { name: "libapplypatch", + host_supported: true, + defaults: [ "applypatch_defaults", ], diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 04b964b17..73701abc9 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -450,9 +450,8 @@ int CacheSizeCheck(size_t bytes) { if (MakeFreeSpaceOnCache(bytes) < 0) { printf("unable to make %zu bytes available on /cache\n", bytes); return 1; - } else { - return 0; } + return 0; } // This function applies binary patches to EMMC target files in a way that is safe (the original @@ -477,7 +476,7 @@ int CacheSizeCheck(size_t bytes) { // become obsolete since we have dropped the support for patching non-EMMC targets (EMMC targets // have the size embedded in the filename). int applypatch(const char* source_filename, const char* target_filename, - const char* target_sha1_str, size_t target_size __unused, + const char* target_sha1_str, size_t /* target_size */, const std::vector& patch_sha1_str, const std::vector>& patch_data, const Value* bonus_data) { printf("patch %s: ", source_filename); diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp index 0a40baa97..ec1d20cec 100644 --- a/applypatch/freecache.cpp +++ b/applypatch/freecache.cpp @@ -111,6 +111,12 @@ static std::set FindExpendableFiles() { } int MakeFreeSpaceOnCache(size_t bytes_needed) { +#ifndef __ANDROID__ + // TODO (xunchang) implement a heuristic cache size check during host simulation. + printf("Skip making (%zu) bytes free space on cache; program is running on host\n", bytes_needed); + return 0; +#endif + size_t free_now = FreeSpaceForFile("/cache"); printf("%zu bytes free on /cache (%zu needed)\n", free_now, bytes_needed); diff --git a/otafault/Android.bp b/otafault/Android.bp index 91a5d9a54..30d561015 100644 --- a/otafault/Android.bp +++ b/otafault/Android.bp @@ -15,6 +15,8 @@ cc_library_static { name: "libotafault", + host_supported: true, + srcs: [ "config.cpp", "ota_io.cpp", -- cgit v1.2.3 From 7a4dacf7245c484f4b602f02ccd470f04535dc62 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Sat, 17 Feb 2018 21:58:54 -0800 Subject: Disable building libapplypatch on mac The sdk_mac on build server fails with the error: bootable/recovery/applypatch/freecache.cpp:23:10: fatal error: 'sys/statfs.h' file not found So we will disable libapplypatch on mac. Test: the library still builds on linux; and check the other host targets in the same cl. Change-Id: Ie4a30708726e51c810f7ad7f1085d38154076cca --- applypatch/Android.bp | 6 ++++++ otafault/Android.bp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/applypatch/Android.bp b/applypatch/Android.bp index d3efa152b..cb0b36746 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -57,6 +57,12 @@ cc_library_static { "libotautil", "libz", ], + + target: { + darwin: { + enabled: false, + }, + }, } cc_library_static { diff --git a/otafault/Android.bp b/otafault/Android.bp index 30d561015..b39d5bee2 100644 --- a/otafault/Android.bp +++ b/otafault/Android.bp @@ -39,6 +39,12 @@ cc_library_static { "-Wthread-safety", "-Wthread-safety-negative", ], + + target: { + darwin: { + enabled: false, + }, + }, } cc_test { -- cgit v1.2.3 From 6cc499ae07129c0c7d75c4063bef29e88424afc9 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Wed, 21 Feb 2018 14:11:02 +0000 Subject: Set LOCAL_SDK_VERSION where possible. This change sets LOCAL_SDK_VERSION for all packages where this is possible without breaking the build, and LOCAL_PRIVATE_PLATFORM_APIS := true otherwise. Setting one of these two will be made required soon, and this is a change in preparation for that. Not setting LOCAL_SDK_VERSION makes the app implicitly depend on the bootclasspath, which is often not required. This change effectively makes depending on private apis opt-in rather than opt-out. Test: make relevant packages Bug: 73535841 Change-Id: I82fee834c5d92e699e9571933faded11c6d4596e --- tools/recovery_l10n/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/recovery_l10n/Android.mk b/tools/recovery_l10n/Android.mk index 937abd1e1..7197c5c78 100644 --- a/tools/recovery_l10n/Android.mk +++ b/tools/recovery_l10n/Android.mk @@ -5,6 +5,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_PACKAGE_NAME := RecoveryLocalizer +LOCAL_SDK_VERSION := current LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) -- cgit v1.2.3 From 0bfea531d89943595405033288ffca6388a0a113 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 21 Feb 2018 17:03:26 -0800 Subject: Import translations. DO NOT MERGE Change-Id: I60c4e4ebce0bf3abcc2b0b5ab90b0fc5e9406c34 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-as/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-or/strings.xml | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 tools/recovery_l10n/res/values-as/strings.xml create mode 100644 tools/recovery_l10n/res/values-or/strings.xml diff --git a/tools/recovery_l10n/res/values-as/strings.xml b/tools/recovery_l10n/res/values-as/strings.xml new file mode 100644 index 000000000..2624cebe4 --- /dev/null +++ b/tools/recovery_l10n/res/values-as/strings.xml @@ -0,0 +1,9 @@ + + + "আপডেইট ইনষ্টল কৰি থকা হৈছে" + "মচি থকা হৈছে" + "কোনো আদেশ নাই" + "ত্ৰুটি!" + "সুৰক্ষা আপডেইট ইনষ্টল কৰি থকা হৈছে" + diff --git a/tools/recovery_l10n/res/values-or/strings.xml b/tools/recovery_l10n/res/values-or/strings.xml new file mode 100644 index 000000000..2b0851cdd --- /dev/null +++ b/tools/recovery_l10n/res/values-or/strings.xml @@ -0,0 +1,9 @@ + + + "ସିଷ୍ଟମ ଅପଡେଟ ଇନଷ୍ଟଲ କରୁଛି" + "ଲିଭାଉଛି" + "କୌଣସି କମାଣ୍ଡ ନାହିଁ" + "ତ୍ରୁଟି!" + "ସୁରକ୍ଷା ଅପ୍‌ଡେଟ୍‌ ଇନ୍‌ଷ୍ଟଲ୍‌ କରୁଛି" + -- cgit v1.2.3 From 1cfb36112901eaf44110d8a29355c3116bee4a3a Mon Sep 17 00:00:00 2001 From: George Burgess IV Date: Sat, 17 Feb 2018 17:48:45 -0800 Subject: Replace vfork() with fork(). The static analyzer complained about the function calls we were doing here to vector::data() and vector::operator[]: bootable/recovery/roots.cpp:193:11: warning: This function call is prohibited after a successful vfork Since it's not clear that vfork() is needed here, just use fork() instead. Bug: None Test: Reran the static analyzer with only this patch applied. Bug disappeared Change-Id: I580f8243a21899f1e1678c8aee6948dfa7f69a1d --- roots.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roots.cpp b/roots.cpp index e2d5d6543..9ff5186c1 100644 --- a/roots.cpp +++ b/roots.cpp @@ -189,7 +189,7 @@ static int exec_cmd(const std::vector& args) { argv.push_back(nullptr); pid_t child; - if ((child = vfork()) == 0) { + if ((child = fork()) == 0) { execv(argv[0], argv.data()); _exit(EXIT_FAILURE); } -- cgit v1.2.3 From 572abbb81cfa12cddf742fa35cd8a4b9eebdc7d1 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 22 Feb 2018 15:40:39 -0800 Subject: Remove the assumption of target chunk size in imgdiff In the split mode of imgdiff, we used to assume that the size of a split target chunk is always greater than the blocksize i.e. 4096. This may lead to the following assertion failure: I0221 04:57:33.451323 818464 common.py:205 imgdiff F 02-21 04:57:33 821203 821203 imgdiff.cpp:999] Check failed: tgt_size >= BLOCK_SIZE (tgt_size=476, BLOCK_SIZE=4096) This CL removes the assumption and handles the edge cases. Test: generate and verify the incremental update for TFs in the bug; unit test passes Bug: 73757557 Bug: 73711365 Change-Id: Iadbb4ee658995f5856cd488f3793980881a59620 --- applypatch/imgdiff.cpp | 45 +++++++----- applypatch/include/applypatch/imgdiff_image.h | 5 +- tests/component/imgdiff_test.cpp | 101 ++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 20 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 3dae63d4b..674cc2b16 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -955,14 +955,17 @@ bool ZipModeImage::SplitZipModeImageWithLimit(const ZipModeImage& tgt_image, tgt->GetRawDataLength()); } } else { - ZipModeImage::AddSplitImageFromChunkList(tgt_image, src_image, src_ranges, split_tgt_chunks, - split_src_chunks, split_tgt_images, - split_src_images); + bool added_image = ZipModeImage::AddSplitImageFromChunkList( + tgt_image, src_image, src_ranges, split_tgt_chunks, split_src_chunks, split_tgt_images, + split_src_images); split_tgt_chunks.clear(); split_src_chunks.clear(); - used_src_ranges.Insert(src_ranges); - split_src_ranges->push_back(std::move(src_ranges)); + // No need to update the split_src_ranges if we don't update the split source images. + if (added_image) { + used_src_ranges.Insert(src_ranges); + split_src_ranges->push_back(std::move(src_ranges)); + } src_ranges.Clear(); // We don't have enough space for the current chunk; start a new split image and handle @@ -973,9 +976,12 @@ bool ZipModeImage::SplitZipModeImageWithLimit(const ZipModeImage& tgt_image, // TODO Trim it in case the CD exceeds limit too much. src_ranges.Insert(central_directory->GetStartOffset(), central_directory->DataLengthForPatch()); - ZipModeImage::AddSplitImageFromChunkList(tgt_image, src_image, src_ranges, split_tgt_chunks, - split_src_chunks, split_tgt_images, split_src_images); - split_src_ranges->push_back(std::move(src_ranges)); + bool added_image = ZipModeImage::AddSplitImageFromChunkList(tgt_image, src_image, src_ranges, + split_tgt_chunks, split_src_chunks, + split_tgt_images, split_src_images); + if (added_image) { + split_src_ranges->push_back(std::move(src_ranges)); + } ValidateSplitImages(*split_tgt_images, *split_src_images, *split_src_ranges, tgt_image.file_content_.size()); @@ -983,7 +989,7 @@ bool ZipModeImage::SplitZipModeImageWithLimit(const ZipModeImage& tgt_image, return true; } -void ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image, +bool ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image, const ZipModeImage& src_image, const SortedRangeSet& split_src_ranges, const std::vector& split_tgt_chunks, @@ -991,12 +997,6 @@ void ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image, std::vector* split_tgt_images, std::vector* split_src_images) { CHECK(!split_tgt_chunks.empty()); - // Target chunks should occupy at least one block. - // TODO put a warning and change the type to raw if it happens in extremely rare cases. - size_t tgt_size = split_tgt_chunks.back().GetStartOffset() + - split_tgt_chunks.back().DataLengthForPatch() - - split_tgt_chunks.front().GetStartOffset(); - CHECK_GE(tgt_size, BLOCK_SIZE); std::vector aligned_tgt_chunks; @@ -1015,7 +1015,12 @@ void ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image, i++; } - CHECK_LT(i, split_tgt_chunks.size()); + + // Nothing left after alignment in the current split tgt chunks; skip adding the split_tgt_image. + if (i == split_tgt_chunks.size()) { + return false; + } + aligned_tgt_chunks.insert(aligned_tgt_chunks.end(), split_tgt_chunks.begin() + i + 1, split_tgt_chunks.end()); CHECK(!aligned_tgt_chunks.empty()); @@ -1024,8 +1029,10 @@ void ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image, size_t end_offset = aligned_tgt_chunks.back().GetStartOffset() + aligned_tgt_chunks.back().GetRawDataLength(); if (end_offset % BLOCK_SIZE != 0 && end_offset < tgt_image.file_content_.size()) { + size_t tail_block_length = std::min(tgt_image.file_content_.size() - end_offset, + BLOCK_SIZE - (end_offset % BLOCK_SIZE)); aligned_tgt_chunks.emplace_back(CHUNK_NORMAL, end_offset, &tgt_image.file_content_, - BLOCK_SIZE - (end_offset % BLOCK_SIZE)); + tail_block_length); } ZipModeImage split_tgt_image(false); @@ -1049,6 +1056,8 @@ void ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image, split_tgt_images->push_back(std::move(split_tgt_image)); split_src_images->push_back(std::move(split_src_image)); + + return true; } void ZipModeImage::ValidateSplitImages(const std::vector& split_tgt_images, @@ -1536,7 +1545,7 @@ int imgdiff(int argc, const char** argv) { " patches together and output them into .\n" " --split-info, Output the split information (patch_size, tgt_size, src_ranges);\n" " zip mode with block-limit only.\n" - " --debug_dir, Debug directory to put the split srcs and patches, zip mode only.\n" + " --debug-dir, Debug directory to put the split srcs and patches, zip mode only.\n" " -v, --verbose, Enable verbose logging."; return 2; } diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h index 0f74420f0..084807237 100644 --- a/applypatch/include/applypatch/imgdiff_image.h +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -265,8 +265,9 @@ class ZipModeImage : public Image { std::vector& split_src_ranges, size_t total_tgt_size); // Construct the dummy split images based on the chunks info and source ranges; and move them into - // the given vectors. - static void AddSplitImageFromChunkList(const ZipModeImage& tgt_image, + // the given vectors. Return true if we add a new split image into |split_tgt_images|, and + // false otherwise. + static bool AddSplitImageFromChunkList(const ZipModeImage& tgt_image, const ZipModeImage& src_image, const SortedRangeSet& split_src_ranges, const std::vector& split_tgt_chunks, diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 728b6cc76..6c23def01 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -969,3 +969,104 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { // Expect 1 piece of patch since limit is larger than the zip file size. GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); } + +TEST(ImgdiffTest, zip_mode_large_apk_small_target_chunk) { + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + // The first entry is less than 4096 bytes, followed immediately by an entry that has a very + // large counterpart in the source file. Therefore the first entry will be patched separately. + std::string small_chunk("a", 2000); + ASSERT_EQ(0, tgt_writer.StartEntry("a", 0)); + ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size())); + ASSERT_EQ(0, tgt_writer.FinishEntry()); + construct_store_entry( + { + { "b", 12, 'b' }, { "c", 3, 'c' }, + }, + &tgt_writer); + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); + ZipWriter src_writer(src_file_ptr); + construct_store_entry({ { "a", 1, 'a' }, { "b", 13, 'b' }, { "c", 1, 'c' } }, &src_writer); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch. + TemporaryFile patch_file; + TemporaryFile split_info_file; + TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); + std::vector args = { + "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect three split src images: + // src_piece 0: a 1 blocks + // src_piece 1: b-0 10 blocks + // src_piece 2: b-1 3 blocks, c 1 blocks, CD + GenerateAndCheckSplitTarget(debug_dir.path, 3, tgt); +} + +TEST(ImgdiffTest, zip_mode_large_apk_skipped_small_target_chunk) { + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + construct_store_entry( + { + { "a", 11, 'a' }, + }, + &tgt_writer); + + // Construct a tiny target entry of 1 byte, which will be skipped due to the tail alignment of + // the previous entry. + std::string small_chunk("b", 1); + ASSERT_EQ(0, tgt_writer.StartEntry("b", 0)); + ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size())); + ASSERT_EQ(0, tgt_writer.FinishEntry()); + + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); + ZipWriter src_writer(src_file_ptr); + construct_store_entry( + { + { "a", 11, 'a' }, { "b", 11, 'b' }, + }, + &src_writer); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch. + TemporaryFile patch_file; + TemporaryFile split_info_file; + TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); + std::vector args = { + "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect two split src images: + // src_piece 0: a-0 10 blocks + // src_piece 1: a-0 1 block, CD + GenerateAndCheckSplitTarget(debug_dir.path, 2, tgt); +} -- cgit v1.2.3 From c2420845391bb8b50cb782c5fee95f0fa643e49c Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 27 Feb 2018 17:05:39 -0800 Subject: Fix the behavior of undefined commands in BlockImageVerify In BlockImageVerify some commands are undefined, e.g. "erase", "new", "zero". And we should not error out if the corresponding function pointer of these commands is null; otherwise we will fail the verification. The old code is: if (cmd->f != nullptr && cmd->f(params) == -1) return false; In the last_command_file change the logic was wrongly modified to if (cmd->f == nullptr) return false; ... if (cmd->f(params) == -1) return false; Test: sideload an incremental OTA twice on bullhead Change-Id: I2561c365badb850da0e416629ccd61f0df7da5d7 --- updater/blockimg.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index feb2aeb27..4f085b204 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1727,9 +1727,11 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, const Command* cmd = cmd_map[params.cmdname]; + // Skip the command if we explicitly set the corresponding function pointer to nullptr, e.g. + // "erase" during block_image_verify. if (cmd->f == nullptr) { - LOG(ERROR) << "failed to find the function for command [" << line << "]"; - goto pbiudone; + LOG(DEBUG) << "skip executing command [" << line << "]"; + continue; } // Skip all commands before the saved last command index when resuming an update. -- cgit v1.2.3 From 3bbb20f557790c015e44098098375eb6cc376a42 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 27 Feb 2018 15:56:11 -0800 Subject: Add a singleton CacheLocation to replace the hard coded locations This class allows us to set the following locations dynamically: cache_temp_source, last_command_file, stash_directory_base. In the updater's main function, we reset the values of these variables to their default locations in /cache; while we can set them to temp files in unit tests or host simulation. Test: unit tests pass Change-Id: I528652650caa41373617ab055d41b1f1a4ec0f87 --- applypatch/applypatch.cpp | 11 +++-- applypatch/freecache.cpp | 3 +- applypatch/include/applypatch/applypatch.h | 6 --- otautil/Android.bp | 1 + otautil/cache_location.cpp | 32 +++++++++++++ otautil/include/otautil/cache_location.h | 72 ++++++++++++++++++++++++++++++ tests/component/applypatch_test.cpp | 5 ++- tests/component/updater_test.cpp | 21 ++++++--- updater/blockimg.cpp | 9 ++-- updater/include/updater/blockimg.h | 1 - updater/updater.cpp | 5 +++ 11 files changed, 139 insertions(+), 27 deletions(-) create mode 100644 otautil/cache_location.cpp create mode 100644 otautil/include/otautil/cache_location.h diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 73701abc9..7645a4005 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -40,10 +40,9 @@ #include "edify/expr.h" #include "otafault/ota_io.h" +#include "otautil/cache_location.h" #include "otautil/print_sha1.h" -std::string cache_temp_source = "/cache/saved.file"; - static int LoadPartitionContents(const std::string& filename, FileContents* file); static size_t FileSink(const unsigned char* data, size_t len, int fd); static int GenerateTarget(const FileContents& source_file, const std::unique_ptr& patch, @@ -404,7 +403,7 @@ int applypatch_check(const char* filename, const std::vector& patch // If the source file is missing or corrupted, it might be because we were killed in the middle // of patching it. A copy of it should have been made in cache_temp_source. If that file // exists and matches the sha1 we're looking for, the check still passes. - if (LoadFileContents(cache_temp_source.c_str(), &file) != 0) { + if (LoadFileContents(CacheLocation::location().cache_temp_source().c_str(), &file) != 0) { printf("failed to load cache file\n"); return 1; } @@ -526,7 +525,7 @@ int applypatch(const char* source_filename, const char* target_filename, printf("source file is bad; trying copy\n"); FileContents copy_file; - if (LoadFileContents(cache_temp_source.c_str(), ©_file) < 0) { + if (LoadFileContents(CacheLocation::location().cache_temp_source().c_str(), ©_file) < 0) { printf("failed to read copy file\n"); return 1; } @@ -621,7 +620,7 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr printf("not enough free space on /cache\n"); return 1; } - if (SaveFileContents(cache_temp_source.c_str(), &source_file) < 0) { + if (SaveFileContents(CacheLocation::location().cache_temp_source().c_str(), &source_file) < 0) { printf("failed to back up source file\n"); return 1; } @@ -667,7 +666,7 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr } // Delete the backup copy of the source. - unlink(cache_temp_source.c_str()); + unlink(CacheLocation::location().cache_temp_source().c_str()); // Success! return 0; diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp index ec1d20cec..ea364d8e6 100644 --- a/applypatch/freecache.cpp +++ b/applypatch/freecache.cpp @@ -33,6 +33,7 @@ #include #include "applypatch/applypatch.h" +#include "otautil/cache_location.h" static int EliminateOpenFiles(std::set* files) { std::unique_ptr d(opendir("/proc"), closedir); @@ -92,7 +93,7 @@ static std::set FindExpendableFiles() { // We can't delete cache_temp_source; if it's there we might have restarted during // installation and could be depending on it to be there. - if (path == cache_temp_source) { + if (path == CacheLocation::location().cache_temp_source()) { continue; } diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index c8ad91505..912ead1fa 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -34,12 +34,6 @@ struct FileContents { std::vector data; }; -// When there isn't enough room on the target filesystem to hold the patched version of the file, -// we copy the original here and delete it to free up space. If the expected source file doesn't -// exist, or is corrupted, we look to see if the cached file contains the bits we want and use it as -// the source instead. The default location for the cached source is "/cache/saved.file". -extern std::string cache_temp_source; - using SinkFn = std::function; // applypatch.cpp diff --git a/otautil/Android.bp b/otautil/Android.bp index 5efb12d60..75cf69148 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -21,6 +21,7 @@ cc_library_static { "SysUtil.cpp", "DirUtil.cpp", "ThermalUtil.cpp", + "cache_location.cpp", "rangeset.cpp", ], diff --git a/otautil/cache_location.cpp b/otautil/cache_location.cpp new file mode 100644 index 000000000..8f289487f --- /dev/null +++ b/otautil/cache_location.cpp @@ -0,0 +1,32 @@ +/* + * 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 "otautil/cache_location.h" + +constexpr const char kDefaultCacheTempSource[] = "/cache/saved.file"; +constexpr const char kDefaultLastCommandFile[] = "/cache/recovery/last_command"; +constexpr const char kDefaultStashDirectoryBase[] = "/cache/recovery"; + +CacheLocation& CacheLocation::location() { + static CacheLocation cache_location; + return cache_location; +} + +void CacheLocation::ResetLocations() { + cache_temp_source_ = kDefaultCacheTempSource; + last_command_file_ = kDefaultLastCommandFile; + stash_directory_base_ = kDefaultStashDirectoryBase; +} diff --git a/otautil/include/otautil/cache_location.h b/otautil/include/otautil/cache_location.h new file mode 100644 index 000000000..85e0d485c --- /dev/null +++ b/otautil/include/otautil/cache_location.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef _OTAUTIL_OTAUTIL_CACHE_LOCATION_H_ +#define _OTAUTIL_OTAUTIL_CACHE_LOCATION_H_ + +#include + +#include "android-base/macros.h" + +// A singleton class to maintain the update related locations. The locations should be only set +// once at the start of the program. +class CacheLocation { + public: + static CacheLocation& location(); + + // Reset the locations to their default values. + void ResetLocations(); + + // getter and setter functions. + std::string cache_temp_source() const { + return cache_temp_source_; + } + void set_cache_temp_source(const std::string& temp_source) { + cache_temp_source_ = temp_source; + } + + std::string last_command_file() const { + return last_command_file_; + } + void set_last_command_file(const std::string& last_command) { + last_command_file_ = last_command; + } + + std::string stash_directory_base() const { + return stash_directory_base_; + } + void set_stash_directory_base(const std::string& base) { + stash_directory_base_ = base; + } + + private: + CacheLocation() {} + DISALLOW_COPY_AND_ASSIGN(CacheLocation); + + // When there isn't enough room on the target filesystem to hold the patched version of the file, + // we copy the original here and delete it to free up space. If the expected source file doesn't + // exist, or is corrupted, we look to see if the cached file contains the bits we want and use it + // as the source instead. The default location for the cached source is "/cache/saved.file". + std::string cache_temp_source_; + + // Location to save the last command that stashes blocks. + std::string last_command_file_; + + // The base directory to write stashes during update. + std::string stash_directory_base_; +}; + +#endif // _OTAUTIL_OTAUTIL_CACHE_LOCATION_H_ diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 21c9a52dc..b6d092557 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -35,6 +35,7 @@ #include "applypatch/applypatch.h" #include "applypatch/applypatch_modes.h" #include "common/test_constants.h" +#include "otautil/cache_location.h" #include "otautil/print_sha1.h" static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { @@ -93,14 +94,14 @@ class ApplyPatchCacheTest : public ApplyPatchTest { protected: void SetUp() override { ApplyPatchTest::SetUp(); - cache_temp_source = old_file; + CacheLocation::location().set_cache_temp_source(old_file); } }; class ApplyPatchModesTest : public ::testing::Test { protected: void SetUp() override { - cache_temp_source = cache_source.path; + CacheLocation::location().set_cache_temp_source(cache_source.path); } TemporaryFile cache_source; diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 448fe4935..5bfd7cb40 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -41,6 +41,7 @@ #include "common/test_constants.h" #include "edify/expr.h" #include "otautil/SysUtil.h" +#include "otautil/cache_location.h" #include "otautil/error_code.h" #include "otautil/print_sha1.h" #include "updater/blockimg.h" @@ -104,7 +105,16 @@ class UpdaterTest : public ::testing::Test { RegisterBuiltins(); RegisterInstallFunctions(); RegisterBlockImageFunctions(); + + // Mock the location of last_command_file. + CacheLocation::location().set_cache_temp_source(temp_saved_source_.path); + CacheLocation::location().set_last_command_file(temp_last_command_.path); + CacheLocation::location().set_stash_directory_base(temp_stash_base_.path); } + + TemporaryFile temp_saved_source_; + TemporaryFile temp_last_command_; + TemporaryDir temp_stash_base_; }; TEST_F(UpdaterTest, getprop) { @@ -542,7 +552,7 @@ TEST_F(UpdaterTest, block_image_update_fail) { expect("", script.c_str(), kNoCause, &updater_info); // Updater generates the stash name based on the input file name. std::string name_digest = get_sha1(update_file.path); - std::string stash_base = "/cache/recovery/" + name_digest; + std::string stash_base = std::string(temp_stash_base_.path) + "/" + name_digest; ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); ASSERT_EQ(-1, access((stash_base + src_hash).c_str(), F_OK)); ASSERT_EQ(0, rmdir(stash_base.c_str())); @@ -709,8 +719,7 @@ TEST_F(UpdaterTest, brotli_new_data) { } TEST_F(UpdaterTest, last_command_update) { - TemporaryFile temp_file; - last_command_file = temp_file.path; + std::string last_command_file = CacheLocation::location().last_command_file(); std::string block1 = std::string(4096, '1'); std::string block2 = std::string(4096, '2'); @@ -797,8 +806,7 @@ TEST_F(UpdaterTest, last_command_update) { } TEST_F(UpdaterTest, last_command_update_unresumable) { - TemporaryFile temp_file; - last_command_file = temp_file.path; + std::string last_command_file = CacheLocation::location().last_command_file(); std::string block1 = std::string(4096, '1'); std::string block2 = std::string(4096, '2'); @@ -853,8 +861,7 @@ TEST_F(UpdaterTest, last_command_update_unresumable) { } TEST_F(UpdaterTest, last_command_verify) { - TemporaryFile temp_file; - last_command_file = temp_file.path; + std::string last_command_file = CacheLocation::location().last_command_file(); std::string block1 = std::string(4096, '1'); std::string block2 = std::string(4096, '2'); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 4f085b204..e93196b27 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -53,6 +53,7 @@ #include "edify/expr.h" #include "otafault/ota_io.h" +#include "otautil/cache_location.h" #include "otautil/error_code.h" #include "otautil/print_sha1.h" #include "otautil/rangeset.h" @@ -65,17 +66,15 @@ #define DEBUG_ERASE 0 static constexpr size_t BLOCKSIZE = 4096; -static constexpr const char* STASH_DIRECTORY_BASE = "/cache/recovery"; static constexpr mode_t STASH_DIRECTORY_MODE = 0700; static constexpr mode_t STASH_FILE_MODE = 0600; -std::string last_command_file = "/cache/recovery/last_command"; - static CauseCode failure_type = kNoCause; static bool is_retry = false; static std::unordered_map stash_map; static void DeleteLastCommandFile() { + std::string last_command_file = CacheLocation::location().last_command_file(); if (unlink(last_command_file.c_str()) == -1 && errno != ENOENT) { PLOG(ERROR) << "Failed to unlink: " << last_command_file; } @@ -84,6 +83,7 @@ static void DeleteLastCommandFile() { // Parse the last command index of the last update and save the result to |last_command_index|. // Return true if we successfully read the index. static bool ParseLastCommandFile(int* last_command_index) { + std::string last_command_file = CacheLocation::location().last_command_file(); android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(last_command_file.c_str(), O_RDONLY))); if (fd == -1) { if (errno != ENOENT) { @@ -119,6 +119,7 @@ static bool ParseLastCommandFile(int* last_command_index) { // Update the last command index in the last_command_file if the current command writes to the // stash either explicitly or implicitly. static bool UpdateLastCommandIndex(int command_index, const std::string& command_string) { + std::string last_command_file = CacheLocation::location().last_command_file(); std::string last_command_tmp = last_command_file + ".tmp"; std::string content = std::to_string(command_index) + "\n" + command_string; android::base::unique_fd wfd( @@ -676,7 +677,7 @@ static std::string GetStashFileName(const std::string& base, const std::string& return ""; } - std::string fn(STASH_DIRECTORY_BASE); + std::string fn(CacheLocation::location().stash_directory_base()); fn += "/" + base + "/" + id + postfix; return fn; diff --git a/updater/include/updater/blockimg.h b/updater/include/updater/blockimg.h index 2cc68ce9d..71733b303 100644 --- a/updater/include/updater/blockimg.h +++ b/updater/include/updater/blockimg.h @@ -19,7 +19,6 @@ #include -extern std::string last_command_file; void RegisterBlockImageFunctions(); #endif diff --git a/updater/updater.cpp b/updater/updater.cpp index f55a0d3bd..f063e5fa1 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -34,6 +34,7 @@ #include "otafault/config.h" #include "otautil/DirUtil.h" #include "otautil/SysUtil.h" +#include "otautil/cache_location.h" #include "otautil/error_code.h" #include "updater/blockimg.h" #include "updater/install.h" @@ -168,6 +169,10 @@ int main(int argc, char** argv) { } ota_io_init(za, state.is_retry); + // Initialize the cache_temp_source, last_command_file and stash_directory_base to their default + // locations. + CacheLocation::location().ResetLocations(); + std::string result; bool status = Evaluate(&state, root, &result); -- cgit v1.2.3 From 01daebbe68943725e7b80e30082330c6bd042a88 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 8 Mar 2018 12:34:19 -0800 Subject: Set the update locations to default in CacheLocation's constructor Otherwise the applypatch executable will fail to back up the source file to /cache when patching the recovery image. Bug: 74198354 Test: run applypatch from boot to recovery (cherry picked from commit b4e3a370bf6fe2bbb6ad8e33d16ce3210595aaef) Change-Id: I37b7fd88d66ab49ef953d4b7dca22577bd1472e1 --- otautil/cache_location.cpp | 9 ++++----- otautil/include/otautil/cache_location.h | 5 +---- updater/updater.cpp | 4 ---- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/otautil/cache_location.cpp b/otautil/cache_location.cpp index 8f289487f..8ddefec5e 100644 --- a/otautil/cache_location.cpp +++ b/otautil/cache_location.cpp @@ -25,8 +25,7 @@ CacheLocation& CacheLocation::location() { return cache_location; } -void CacheLocation::ResetLocations() { - cache_temp_source_ = kDefaultCacheTempSource; - last_command_file_ = kDefaultLastCommandFile; - stash_directory_base_ = kDefaultStashDirectoryBase; -} +CacheLocation::CacheLocation() + : cache_temp_source_(kDefaultCacheTempSource), + last_command_file_(kDefaultLastCommandFile), + stash_directory_base_(kDefaultStashDirectoryBase) {} diff --git a/otautil/include/otautil/cache_location.h b/otautil/include/otautil/cache_location.h index 85e0d485c..f2f663816 100644 --- a/otautil/include/otautil/cache_location.h +++ b/otautil/include/otautil/cache_location.h @@ -27,9 +27,6 @@ class CacheLocation { public: static CacheLocation& location(); - // Reset the locations to their default values. - void ResetLocations(); - // getter and setter functions. std::string cache_temp_source() const { return cache_temp_source_; @@ -53,7 +50,7 @@ class CacheLocation { } private: - CacheLocation() {} + CacheLocation(); DISALLOW_COPY_AND_ASSIGN(CacheLocation); // When there isn't enough room on the target filesystem to hold the patched version of the file, diff --git a/updater/updater.cpp b/updater/updater.cpp index f063e5fa1..1d6b172bb 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -169,10 +169,6 @@ int main(int argc, char** argv) { } ota_io_init(za, state.is_retry); - // Initialize the cache_temp_source, last_command_file and stash_directory_base to their default - // locations. - CacheLocation::location().ResetLocations(); - std::string result; bool status = Evaluate(&state, root, &result); -- cgit v1.2.3 From 91b48f9161a918e4af6233af7fa842b9af0a6bac Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Tue, 6 Mar 2018 17:59:58 -0800 Subject: recovery: add libhidl-gen-utils depedency introduced as a depedency to libvintf. Test: builds Bug: 73556059 Change-Id: Ia51ba81ef462879481dcacb80d9ea9ea35e8b0bb Merged-In: Ia51ba81ef462879481dcacb80d9ea9ea35e8b0bb --- Android.mk | 1 + tests/Android.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/Android.mk b/Android.mk index 199cee7ec..461ad9449 100644 --- a/Android.mk +++ b/Android.mk @@ -179,6 +179,7 @@ LOCAL_STATIC_LIBRARIES += \ libcrypto \ libvintf_recovery \ libvintf \ + libhidl-gen-utils \ libtinyxml2 \ libbase \ libutils \ diff --git a/tests/Android.mk b/tests/Android.mk index d911c25e4..b3584fe87 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -147,6 +147,7 @@ LOCAL_STATIC_LIBRARIES := \ libfs_mgr \ libvintf_recovery \ libvintf \ + libhidl-gen-utils \ libtinyxml2 \ libselinux \ libext4_utils \ -- cgit v1.2.3 From d26f95287a0efc6a3160624fa4e577891a2c9794 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 12 Mar 2018 21:18:52 -0700 Subject: tests: Add ApplyPatchModesTest.PatchModeEmmcTargetWithBsdiffPatch test. /system/bin/applypatch on device is expected to work with bsdiff based recovery-from-boot patch automatically. Adding a test to ensure that's always the case. Bug: 72731506 Test: Run recovery_component_test on marlin. Change-Id: I56283cd3ce7cf0215cc3bb3619b206fa01d552c4 Merged-In: I56283cd3ce7cf0215cc3bb3619b206fa01d552c4 (cherry picked from commit d612b23dfd58dbe5059ba53d8fd13cbb343b177c) --- tests/component/applypatch_test.cpp | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index b6d092557..61e06adb6 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "applypatch/applypatch.h" @@ -38,6 +39,8 @@ #include "otautil/cache_location.h" #include "otautil/print_sha1.h" +using namespace std::string_literals; + static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { ASSERT_NE(nullptr, sha1); @@ -265,6 +268,54 @@ TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) { ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data())); } +// Ensures that applypatch works with a bsdiff based recovery-from-boot.p. +TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) { + std::string boot_img_file = from_testdata_base("boot.img"); + std::string boot_img_sha1; + size_t boot_img_size; + sha1sum(boot_img_file, &boot_img_sha1, &boot_img_size); + + std::string recovery_img_file = from_testdata_base("recovery.img"); + std::string recovery_img_sha1; + size_t recovery_img_size; + sha1sum(recovery_img_file, &recovery_img_sha1, &recovery_img_size); + + // Generate the bsdiff patch of recovery-from-boot.p. + std::string src_content; + ASSERT_TRUE(android::base::ReadFileToString(boot_img_file, &src_content)); + + std::string tgt_content; + ASSERT_TRUE(android::base::ReadFileToString(recovery_img_file, &tgt_content)); + + TemporaryFile patch_file; + ASSERT_EQ(0, + bsdiff::bsdiff(reinterpret_cast(src_content.data()), src_content.size(), + reinterpret_cast(tgt_content.data()), tgt_content.size(), + patch_file.path, nullptr)); + + // applypatch : + std::string src_file_arg = + "EMMC:" + boot_img_file + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1; + TemporaryFile tgt_file; + std::string tgt_file_arg = "EMMC:"s + tgt_file.path; + std::string recovery_img_size_arg = std::to_string(recovery_img_size); + std::string patch_arg = boot_img_sha1 + ":" + patch_file.path; + std::vector args = { "applypatch", + src_file_arg.c_str(), + tgt_file_arg.c_str(), + recovery_img_sha1.c_str(), + recovery_img_size_arg.c_str(), + patch_arg.c_str() }; + ASSERT_EQ(0, applypatch_modes(args.size(), args.data())); + + // Double check the patched recovery image. + std::string tgt_file_sha1; + size_t tgt_file_size; + sha1sum(tgt_file.path, &tgt_file_sha1, &tgt_file_size); + ASSERT_EQ(recovery_img_size, tgt_file_size); + ASSERT_EQ(recovery_img_sha1, tgt_file_sha1); +} + TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) { // Invalid bonus file. ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" })); -- cgit v1.2.3 From 454d8cb805e633aaa1c6456a57ec9c2dc6c6e163 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 22 Mar 2018 16:07:00 -0700 Subject: update_verifier: Support verifying product partition. We have added the support for building /product partition in build system (the CL in [1]), where /product is an optional partition that contains system files. This CL adds the matching support if /product needs to be verified during A/B OTA (i.e. listed in care_map file). [1]: commit b7735d81054002961b681f4bdf296d4de2701135, https://android-review.googlesource.com/c/platform/build/+/598454 Bug: 63974895 Test: Run update_verifier test on walleye. Change-Id: Ia1c35e9583b8e66c98a4495b1f81a5ea7e65036f (cherry picked from commit ec2e8c6c1ef3cbafa129ade95abca3203e062b5f) --- tests/component/update_verifier_test.cpp | 3 +-- update_verifier/update_verifier.cpp | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index b04e1185e..1544bb2a4 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -46,7 +46,6 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) { return; } - // The care map file can have only two or four lines. TemporaryFile temp_file; std::string content = "system\n2,0,1"; ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); @@ -58,7 +57,7 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) { } TEST_F(UpdateVerifierTest, verify_image_wrong_lines) { - // The care map file can have only two or four lines. + // The care map file can have only 2 / 4 / 6 lines. TemporaryFile temp_file; ASSERT_FALSE(verify_image(temp_file.path)); diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index c5e154f03..92d931371 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -74,14 +74,13 @@ static int dm_name_filter(const dirent* de) { } static bool read_blocks(const std::string& partition, const std::string& range_str) { - if (partition != "system" && partition != "vendor") { - LOG(ERROR) << "partition name must be system or vendor: " << partition; + if (partition != "system" && partition != "vendor" && partition != "product") { + LOG(ERROR) << "Invalid partition name \"" << partition << "\""; return false; } - // Iterate the content of "/sys/block/dm-X/dm/name". If it matches "system" - // (or "vendor"), then dm-X is a dm-wrapped system/vendor partition. - // Afterwards, update_verifier will read every block on the care_map_file of - // "/dev/block/dm-X" to ensure the partition's integrity. + // Iterate the content of "/sys/block/dm-X/dm/name". If it matches one of "system", "vendor" or + // "product", then dm-X is a dm-wrapped device for that target. We will later read all the + // ("cared") blocks from "/dev/block/dm-X" to ensure the target partition's integrity. static constexpr auto DM_PATH_PREFIX = "/sys/block/"; dirent** namelist; int n = scandir(DM_PATH_PREFIX, &namelist, dm_name_filter, alphasort); @@ -206,10 +205,9 @@ bool verify_image(const std::string& care_map_name) { PLOG(WARNING) << "Failed to open " << care_map_name; return true; } - // Care map file has four lines (two lines if vendor partition is not present): - // First line has the block partition name (system/vendor). - // Second line holds all ranges of blocks to verify. - // The next two lines have the same format but for vendor partition. + // 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. std::string file_content; if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) { LOG(ERROR) << "Error reading care map contents to string."; @@ -218,9 +216,9 @@ bool verify_image(const std::string& care_map_name) { std::vector lines; lines = android::base::Split(android::base::Trim(file_content), "\n"); - if (lines.size() != 2 && lines.size() != 4) { + if (lines.size() != 2 && lines.size() != 4 && lines.size() != 6) { LOG(ERROR) << "Invalid lines in care_map: found " << lines.size() - << " lines, expecting 2 or 4 lines."; + << " lines, expecting 2 or 4 or 6 lines."; return false; } -- cgit v1.2.3 From 10b3faa10db9ea1af66f2ae8ecdf9225f882f7f6 Mon Sep 17 00:00:00 2001 From: Yi Kong Date: Tue, 3 Apr 2018 12:45:45 -0700 Subject: Use non-LTO variant of libhwbinder ... as a workaround for build system does not currently infer non-LTO usage from Android.mk. Test: m Bug: 77320844 Change-Id: I6e9954b895051cc20408a30d0761e164e4c5bfd9 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 461ad9449..4f7ced268 100644 --- a/Android.mk +++ b/Android.mk @@ -156,7 +156,7 @@ LOCAL_STATIC_LIBRARIES := \ libhealthstoragedefault \ libhidltransport \ libhidlbase \ - libhwbinder \ + libhwbinder_nolto \ libvndksupport \ libbatterymonitor -- cgit v1.2.3 From 4358262b757a39fe7939369be4afa6869e8caa3a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 2 Apr 2018 13:37:35 -0700 Subject: mkfs.f2fs: specify sector size for target image size The total sectors that we want to format is used in different meanings from various users. This notifies its size based on 4096 bytes explicitly. Bug: 76407663 Change-Id: I3392646648264ad1ca78e4b87240edc9385a0cc4 Merged-In: I3392646648264ad1ca78e4b87240edc9385a0cc4 Reported-by: katao@xiaomi.com Signed-off-by: Jaegeuk Kim --- roots.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/roots.cpp b/roots.cpp index 9ff5186c1..8907bbd9b 100644 --- a/roots.cpp +++ b/roots.cpp @@ -324,19 +324,34 @@ int format_volume(const char* volume, const char* directory) { } // Has to be f2fs because we checked earlier. + static constexpr int kSectorSize = 4096; std::string cmd("/sbin/mkfs.f2fs"); - std::vector make_f2fs_cmd = { cmd, "-d1", "-f", "-O", - "encrypt", "-O", "quota", v->blk_device }; - if (length >= 512) { - make_f2fs_cmd.push_back(std::to_string(length / 512)); + // clang-format off + std::vector make_f2fs_cmd = { + cmd, + "-d1", + "-f", + "-O", "encrypt", + "-O", "quota", + "-w", std::to_string(kSectorSize), + v->blk_device, + }; + // clang-format on + if (length >= kSectorSize) { + make_f2fs_cmd.push_back(std::to_string(length / kSectorSize)); } int result = exec_cmd(make_f2fs_cmd); if (result == 0 && directory != nullptr) { cmd = "/sbin/sload.f2fs"; + // clang-format off std::vector sload_f2fs_cmd = { - cmd, "-f", directory, "-t", volume, v->blk_device, + cmd, + "-f", directory, + "-t", volume, + v->blk_device, }; + // clang-format on result = exec_cmd(sload_f2fs_cmd); } if (result != 0) { -- cgit v1.2.3 From 848f227eea524a48208bdd4181f90fb63a31ed2b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 8 Dec 2017 13:19:23 -0800 Subject: f2fs: support f2fs by setting unmovable bit for package file This enables to use uncrypt for f2fs update-on-reboot. It requires kernel patch named: "f2fs: add an ioctl to disable GC for specific file" If any operation fails during uncrypt, please delete package file as soon as possible, and create the file again to move forward. IOWs, don't leave the package file for a long time. Bug: 70309376 Bug: 30170612 Change-Id: I3b4233e7da756f107be35364521699deaf2e7139 Merged-In: I3b4233e7da756f107be35364521699deaf2e7139 Signed-off-by: Jaegeuk Kim --- uncrypt/uncrypt.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 645faadbf..bb43c2c4a 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -172,10 +172,14 @@ static struct fstab* read_fstab() { return fstab; } -static const char* find_block_device(const char* path, bool* encryptable, bool* encrypted) { +static const char* find_block_device(const char* path, bool* encryptable, bool* encrypted, bool *f2fs_fs) { // Look for a volume whose mount point is the prefix of path and // return its block device. Set encrypted if it's currently // encrypted. + + // ensure f2fs_fs is set to 0 first. + if (f2fs_fs) + *f2fs_fs = false; for (int i = 0; i < fstab->num_entries; ++i) { struct fstab_rec* v = &fstab->recs[i]; if (!v->mount_point) { @@ -192,6 +196,8 @@ static const char* find_block_device(const char* path, bool* encryptable, bool* *encrypted = true; } } + if (f2fs_fs && strcmp(v->fs_type, "f2fs") == 0) + *f2fs_fs = true; return v->blk_device; } } @@ -244,7 +250,7 @@ static int retry_fibmap(const int fd, const char* name, int* block, const int he } static int produce_block_map(const char* path, const char* map_file, const char* blk_dev, - bool encrypted, int socket) { + bool encrypted, bool f2fs_fs, int socket) { std::string err; if (!android::base::RemoveFileIfExists(map_file, &err)) { LOG(ERROR) << "failed to remove the existing map file " << map_file << ": " << err; @@ -307,6 +313,17 @@ static int produce_block_map(const char* path, const char* map_file, const char* } } +#ifndef F2FS_IOC_SET_DONTMOVE +#ifndef F2FS_IOCTL_MAGIC +#define F2FS_IOCTL_MAGIC 0xf5 +#endif +#define F2FS_IOC_SET_DONTMOVE _IO(F2FS_IOCTL_MAGIC, 13) +#endif + if (f2fs_fs && ioctl(fd, F2FS_IOC_SET_DONTMOVE) < 0) { + PLOG(ERROR) << "Failed to set non-movable file for f2fs: " << path << " on " << blk_dev; + return kUncryptIoctlError; + } + off64_t pos = 0; int last_progress = 0; while (pos < sb.st_size) { @@ -458,7 +475,8 @@ static int uncrypt(const char* input_path, const char* map_file, const int socke bool encryptable; bool encrypted; - const char* blk_dev = find_block_device(path, &encryptable, &encrypted); + bool f2fs_fs; + const char* blk_dev = find_block_device(path, &encryptable, &encrypted, &f2fs_fs); if (blk_dev == nullptr) { LOG(ERROR) << "failed to find block device for " << path; return kUncryptBlockDeviceFindError; @@ -479,7 +497,7 @@ static int uncrypt(const char* input_path, const char* map_file, const int socke // and /sdcard we leave the file alone. if (strncmp(path, "/data/", 6) == 0) { LOG(INFO) << "writing block map " << map_file; - return produce_block_map(path, map_file, blk_dev, encrypted, socket); + return produce_block_map(path, map_file, blk_dev, encrypted, f2fs_fs, socket); } return 0; -- cgit v1.2.3 From 1224544806382ca98bbb7d9edba83b0c2e1be21f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 4 Apr 2018 21:14:01 -0700 Subject: install: mkfs.f2fs: specify sector size for target image size The total sectors that we want to format is used in different meanings from various users. This notifies its size based on 512 bytes explicitly. Bug: 76407663 Change-Id: I20687b40a1733d3b459a45f8b64a338c37a7bc95 Signed-off-by: Jaegeuk Kim --- updater/install.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/updater/install.cpp b/updater/install.cpp index 2b6c20fe3..6732ab897 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -548,6 +548,8 @@ Value* FormatFn(const char* name, State* state, const std::vector Date: Thu, 5 Apr 2018 22:42:13 -0700 Subject: recovery: enable fsverity feature bit Bug: 74604441 Bug 67380979 Change-Id: Iab1cc9aef356f0ddf6e2491578a2bd53009182ce Signed-off-by: Jaegeuk Kim --- roots.cpp | 1 + updater/install.cpp | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/roots.cpp b/roots.cpp index 8907bbd9b..184e7992b 100644 --- a/roots.cpp +++ b/roots.cpp @@ -333,6 +333,7 @@ int format_volume(const char* volume, const char* directory) { "-f", "-O", "encrypt", "-O", "quota", + "-O", "verity", "-w", std::to_string(kSectorSize), v->blk_device, }; diff --git a/updater/install.cpp b/updater/install.cpp index 6732ab897..9be7645f3 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -544,12 +544,10 @@ Value* FormatFn(const char* name, State* state, const std::vector Date: Mon, 9 Apr 2018 10:38:24 -0700 Subject: Use the non-LTO, non-PGO version of libhwbinder Bug: http://b/77320844 Android.mk doesn't have support for PGO either. This causes linker error (about missing __llvm_profile symbols) in the PGO-instrumentation enabled builds in the release branch. Test: m ANDROID_PGO_INSTRUMENT=ALL Change-Id: Ib5dfdea88073bd95da2cd02e92c85bfffcca80d6 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 4f7ced268..7e0ad122e 100644 --- a/Android.mk +++ b/Android.mk @@ -156,7 +156,7 @@ LOCAL_STATIC_LIBRARIES := \ libhealthstoragedefault \ libhidltransport \ libhidlbase \ - libhwbinder_nolto \ + libhwbinder_noltopgo \ libvndksupport \ libbatterymonitor -- cgit v1.2.3 From 84c82a8142517f30040bf16bed045eb3b309e967 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Fri, 13 Apr 2018 16:08:01 -0700 Subject: minadbd: track signature change of service_to_fd. Bug: http://b/37066218 Bug: http://b/71898863 Test: treehugger Change-Id: I5f2b14c65cff8d41dd3230d78b87e3e27e489bf6 (cherry picked from commit 570b08b7904901162ae2d4c847b19c1aa9738aaf) --- minadbd/minadbd_services.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 9f0f1f87d..043c51a6a 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -58,7 +58,7 @@ static int create_service_thread(void (*func)(int, const std::string&), const st return s[0]; } -int service_to_fd(const char* name, const atransport* /* transport */) { +int service_to_fd(const char* name, atransport* /* transport */) { int ret = -1; if (!strncmp(name, "sideload:", 9)) { -- cgit v1.2.3 From 723056a83f8c8b15af02d9c302862dbb2304ea8c Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Wed, 8 Jun 2016 13:51:41 -0700 Subject: Wipe the metadata partition when we wipe data. Bug: 78469201 Test: Wipe from recovery menu, check that wipe is logged correctly and boot works as expected. Merged-In: I5bc8ef1b83d78de8b5edba6cc17882edcc744356 Change-Id: I5bc8ef1b83d78de8b5edba6cc17882edcc744356 --- recovery.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index d887d07fb..07ec5cfb6 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -108,6 +108,7 @@ static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe"; static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; static const char *CACHE_ROOT = "/cache"; static const char *DATA_ROOT = "/data"; +static const char* METADATA_ROOT = "/metadata"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; @@ -752,11 +753,19 @@ static bool wipe_data(Device* device) { modified_flash = true; ui->Print("\n-- Wiping data...\n"); - bool success = - device->PreWipeData() && - erase_volume("/data") && - (has_cache ? erase_volume("/cache") : true) && - device->PostWipeData(); + 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; } -- cgit v1.2.3