Loading applypatch/applypatch.cpp +270 −282 Original line number Diff line number Diff line Loading @@ -194,17 +194,16 @@ static int LoadPartitionContents(const std::string& filename, FileContents* file // Save the contents of the given FileContents object under the given // filename. Return 0 on success. int SaveFileContents(const char* filename, const FileContents* file) { int fd = ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); if (fd < 0) { unique_fd fd(ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR)); if (fd == -1) { printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno)); return -1; } ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); if (bytes_written != static_cast<ssize_t>(file->data.size())) { printf("short write of \"%s\" (%zd bytes of %zu) (%s)\n", filename, bytes_written, file->data.size(), strerror(errno)); ota_close(fd); printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written, file->data.size(), strerror(errno)); return -1; } if (ota_fsync(fd) != 0) { Loading Loading @@ -232,24 +231,22 @@ int SaveFileContents(const char* filename, const FileContents* file) { // "EMMC:<partition_device>[:...]". The target name // might contain multiple colons, but WriteToPartition() only uses the first // two and ignores the rest. Return 0 on success. int WriteToPartition(const unsigned char* data, size_t len, const char* target) { std::vector<std::string> pieces = android::base::Split(std::string(target), ":"); int WriteToPartition(const unsigned char* data, size_t len, const std::string& target) { std::vector<std::string> pieces = android::base::Split(target, ":"); if (pieces.size() < 2 || pieces[0] != "EMMC") { printf("WriteToPartition called with bad target (%s)\n", target); printf("WriteToPartition called with bad target (%s)\n", target.c_str()); return -1; } const char* partition = pieces[1].c_str(); size_t start = 0; bool success = false; int fd = ota_open(partition, O_RDWR | O_SYNC); if (fd < 0) { unique_fd fd(ota_open(partition, O_RDWR)); if (fd == -1) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } size_t start = 0; bool success = false; for (size_t attempt = 0; attempt < 2; ++attempt) { if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { printf("failed seek on %s: %s\n", partition, strerror(errno)); Loading @@ -268,23 +265,23 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) } if (ota_fsync(fd) != 0) { printf("failed to sync to %s (%s)\n", partition, strerror(errno)); printf("failed to sync to %s: %s\n", partition, strerror(errno)); return -1; } if (ota_close(fd) != 0) { printf("failed to close %s (%s)\n", partition, strerror(errno)); printf("failed to close %s: %s\n", partition, strerror(errno)); return -1; } fd = ota_open(partition, O_RDONLY); if (fd < 0) { printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno)); fd.reset(ota_open(partition, O_RDONLY)); if (fd == -1) { printf("failed to reopen %s for verify: %s\n", partition, strerror(errno)); return -1; } // Drop caches so our subsequent verification read won't just be reading the cache. sync(); int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY); unique_fd dc(ota_open("/proc/sys/vm/drop_caches", O_WRONLY)); if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); } else { Loading @@ -293,7 +290,7 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) ota_close(dc); sleep(1); // verify // Verify. if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { printf("failed to seek back to beginning of %s: %s\n", partition, strerror(errno)); return -1; Loading @@ -318,8 +315,7 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) return -1; } if (static_cast<size_t>(read_count) < to_read) { printf("short verify read %s at %zu: %zd %zu %s\n", partition, p, read_count, to_read, strerror(errno)); printf("short verify read %s at %zu: %zd %zu\n", partition, p, read_count, to_read); } so_far += read_count; } Loading @@ -343,8 +339,8 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) return -1; } if (ota_close(fd) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); if (ota_close(fd) == -1) { printf("error closing %s: %s\n", partition, strerror(errno)); return -1; } sync(); Loading Loading @@ -595,7 +591,6 @@ int applypatch_flash(const char* source_filename, const char* target_filename, } std::string target_str(target_filename); std::vector<std::string> pieces = android::base::Split(target_str, ":"); if (pieces.size() != 2 || pieces[0] != "EMMC") { printf("invalid target name \"%s\"", target_filename); Loading Loading @@ -641,15 +636,6 @@ static int GenerateTarget(FileContents* source_file, const uint8_t target_sha1[SHA_DIGEST_LENGTH], size_t target_size, const Value* bonus_data) { int retry = 1; SHA_CTX ctx; std::string memory_sink_str; FileContents* source_to_use; int made_copy = 0; bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0); const std::string tmp_target_filename = std::string(target_filename) + ".patch"; // assume that target_filename (eg "/system/app/Foo.apk") is located // on the same filesystem as its top-level directory ("/system"). // We need something that exists for calling statfs(). Loading @@ -659,18 +645,21 @@ static int GenerateTarget(FileContents* source_file, target_fs.resize(slash_pos); } FileContents* source_to_use; const Value* patch; if (source_patch_value != NULL) { if (source_patch_value != nullptr) { source_to_use = source_file; patch = source_patch_value; } else { source_to_use = copy_file; patch = copy_patch_value; } if (patch->type != VAL_BLOB) { printf("patch is not a blob\n"); return 1; } const char* header = &patch->data[0]; size_t header_bytes_read = patch->data.size(); bool use_bsdiff = false; Loading @@ -683,9 +672,15 @@ static int GenerateTarget(FileContents* source_file, return 1; } bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0); const std::string tmp_target_filename = std::string(target_filename) + ".patch"; int retry = 1; bool made_copy = false; SHA_CTX ctx; std::string memory_sink_str; // Don't need to reserve space. do { // Is there enough room in the target filesystem to hold the patched // file? // Is there enough room in the target filesystem to hold the patched file? if (target_is_partition) { // If the target is a partition, we're actually going to Loading @@ -704,18 +699,17 @@ static int GenerateTarget(FileContents* source_file, printf("failed to back up source file\n"); return 1; } made_copy = 1; made_copy = true; retry = 0; } else { int enough_space = 0; bool enough_space = false; if (retry > 0) { size_t free_space = FreeSpaceForFile(target_fs.c_str()); enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum (free_space > (target_size * 3 / 2)); // 50% margin of error if (!enough_space) { printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n", target_size, free_space, retry, enough_space); printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n", target_size, free_space, retry, enough_space); } } Loading @@ -723,7 +717,7 @@ static int GenerateTarget(FileContents* source_file, retry = 0; } if (!enough_space && source_patch_value != NULL) { if (!enough_space && source_patch_value != nullptr) { // Using the original source, but not enough free space. First // copy the source file to cache, then delete it from the original // location. Loading @@ -745,7 +739,7 @@ static int GenerateTarget(FileContents* source_file, printf("failed to back up source file\n"); return 1; } made_copy = 1; made_copy = true; unlink(source_filename); size_t free_space = FreeSpaceForFile(target_fs.c_str()); Loading @@ -753,48 +747,43 @@ static int GenerateTarget(FileContents* source_file, } } SinkFn sink = NULL; void* token = NULL; int output_fd = -1; SinkFn sink = nullptr; void* token = nullptr; unique_fd output_fd; if (target_is_partition) { // We store the decoded output in memory. sink = MemorySink; token = &memory_sink_str; } else { // We write the decoded output to "<tgt-file>.patch". output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); if (output_fd < 0) { printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), strerror(errno)); output_fd.reset(ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR)); if (output_fd == -1) { printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } sink = FileSink; token = &output_fd; } SHA1_Init(&ctx); int result; if (use_bsdiff) { result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(), patch, 0, sink, token, &ctx); result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(), patch, 0, sink, token, &ctx); } else { result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(), patch, sink, token, &ctx, bonus_data); result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(), patch, sink, token, &ctx, bonus_data); } if (!target_is_partition) { if (ota_fsync(output_fd) != 0) { printf("failed to fsync file \"%s\" (%s)\n", tmp_target_filename.c_str(), strerror(errno)); printf("failed to fsync file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno)); result = 1; } if (ota_close(output_fd) != 0) { printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(), strerror(errno)); printf("failed to close file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno)); result = 1; } } Loading @@ -802,7 +791,7 @@ static int GenerateTarget(FileContents* source_file, if (result != 0) { if (retry == 0) { printf("applying patch failed\n"); return result != 0; return 1; } else { printf("applying patch failed; retrying\n"); } Loading Loading @@ -832,13 +821,13 @@ static int GenerateTarget(FileContents* source_file, return 1; } } else { // Give the .patch file the same owner, group, and mode of the // original source file. // Give the .patch file the same owner, group, and mode of the original source file. if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) { printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) { if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) { printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } Loading @@ -850,8 +839,7 @@ static int GenerateTarget(FileContents* source_file, } } // If this run of applypatch created the copy, and we're here, we // can delete it. // If this run of applypatch created the copy, and we're here, we can delete it. if (made_copy) { unlink(CACHE_TEMP_SOURCE); } Loading otafault/ota_io.h +10 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <stdio.h> #include <sys/stat.h> #include <android-base/unique_fd.h> #define OTAIO_CACHE_FNAME "/cache/saved.file" void ota_set_fault_files(); Loading @@ -50,4 +52,12 @@ ssize_t ota_write(int fd, const void* buf, size_t nbyte); int ota_fsync(int fd); struct OtaCloser { static void Close(int fd) { ota_close(fd); } }; using unique_fd = android::base::unique_fd_impl<OtaCloser>; #endif Loading
applypatch/applypatch.cpp +270 −282 Original line number Diff line number Diff line Loading @@ -194,17 +194,16 @@ static int LoadPartitionContents(const std::string& filename, FileContents* file // Save the contents of the given FileContents object under the given // filename. Return 0 on success. int SaveFileContents(const char* filename, const FileContents* file) { int fd = ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); if (fd < 0) { unique_fd fd(ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR)); if (fd == -1) { printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno)); return -1; } ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); if (bytes_written != static_cast<ssize_t>(file->data.size())) { printf("short write of \"%s\" (%zd bytes of %zu) (%s)\n", filename, bytes_written, file->data.size(), strerror(errno)); ota_close(fd); printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written, file->data.size(), strerror(errno)); return -1; } if (ota_fsync(fd) != 0) { Loading Loading @@ -232,24 +231,22 @@ int SaveFileContents(const char* filename, const FileContents* file) { // "EMMC:<partition_device>[:...]". The target name // might contain multiple colons, but WriteToPartition() only uses the first // two and ignores the rest. Return 0 on success. int WriteToPartition(const unsigned char* data, size_t len, const char* target) { std::vector<std::string> pieces = android::base::Split(std::string(target), ":"); int WriteToPartition(const unsigned char* data, size_t len, const std::string& target) { std::vector<std::string> pieces = android::base::Split(target, ":"); if (pieces.size() < 2 || pieces[0] != "EMMC") { printf("WriteToPartition called with bad target (%s)\n", target); printf("WriteToPartition called with bad target (%s)\n", target.c_str()); return -1; } const char* partition = pieces[1].c_str(); size_t start = 0; bool success = false; int fd = ota_open(partition, O_RDWR | O_SYNC); if (fd < 0) { unique_fd fd(ota_open(partition, O_RDWR)); if (fd == -1) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } size_t start = 0; bool success = false; for (size_t attempt = 0; attempt < 2; ++attempt) { if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { printf("failed seek on %s: %s\n", partition, strerror(errno)); Loading @@ -268,23 +265,23 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) } if (ota_fsync(fd) != 0) { printf("failed to sync to %s (%s)\n", partition, strerror(errno)); printf("failed to sync to %s: %s\n", partition, strerror(errno)); return -1; } if (ota_close(fd) != 0) { printf("failed to close %s (%s)\n", partition, strerror(errno)); printf("failed to close %s: %s\n", partition, strerror(errno)); return -1; } fd = ota_open(partition, O_RDONLY); if (fd < 0) { printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno)); fd.reset(ota_open(partition, O_RDONLY)); if (fd == -1) { printf("failed to reopen %s for verify: %s\n", partition, strerror(errno)); return -1; } // Drop caches so our subsequent verification read won't just be reading the cache. sync(); int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY); unique_fd dc(ota_open("/proc/sys/vm/drop_caches", O_WRONLY)); if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); } else { Loading @@ -293,7 +290,7 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) ota_close(dc); sleep(1); // verify // Verify. if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { printf("failed to seek back to beginning of %s: %s\n", partition, strerror(errno)); return -1; Loading @@ -318,8 +315,7 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) return -1; } if (static_cast<size_t>(read_count) < to_read) { printf("short verify read %s at %zu: %zd %zu %s\n", partition, p, read_count, to_read, strerror(errno)); printf("short verify read %s at %zu: %zd %zu\n", partition, p, read_count, to_read); } so_far += read_count; } Loading @@ -343,8 +339,8 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) return -1; } if (ota_close(fd) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); if (ota_close(fd) == -1) { printf("error closing %s: %s\n", partition, strerror(errno)); return -1; } sync(); Loading Loading @@ -595,7 +591,6 @@ int applypatch_flash(const char* source_filename, const char* target_filename, } std::string target_str(target_filename); std::vector<std::string> pieces = android::base::Split(target_str, ":"); if (pieces.size() != 2 || pieces[0] != "EMMC") { printf("invalid target name \"%s\"", target_filename); Loading Loading @@ -641,15 +636,6 @@ static int GenerateTarget(FileContents* source_file, const uint8_t target_sha1[SHA_DIGEST_LENGTH], size_t target_size, const Value* bonus_data) { int retry = 1; SHA_CTX ctx; std::string memory_sink_str; FileContents* source_to_use; int made_copy = 0; bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0); const std::string tmp_target_filename = std::string(target_filename) + ".patch"; // assume that target_filename (eg "/system/app/Foo.apk") is located // on the same filesystem as its top-level directory ("/system"). // We need something that exists for calling statfs(). Loading @@ -659,18 +645,21 @@ static int GenerateTarget(FileContents* source_file, target_fs.resize(slash_pos); } FileContents* source_to_use; const Value* patch; if (source_patch_value != NULL) { if (source_patch_value != nullptr) { source_to_use = source_file; patch = source_patch_value; } else { source_to_use = copy_file; patch = copy_patch_value; } if (patch->type != VAL_BLOB) { printf("patch is not a blob\n"); return 1; } const char* header = &patch->data[0]; size_t header_bytes_read = patch->data.size(); bool use_bsdiff = false; Loading @@ -683,9 +672,15 @@ static int GenerateTarget(FileContents* source_file, return 1; } bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0); const std::string tmp_target_filename = std::string(target_filename) + ".patch"; int retry = 1; bool made_copy = false; SHA_CTX ctx; std::string memory_sink_str; // Don't need to reserve space. do { // Is there enough room in the target filesystem to hold the patched // file? // Is there enough room in the target filesystem to hold the patched file? if (target_is_partition) { // If the target is a partition, we're actually going to Loading @@ -704,18 +699,17 @@ static int GenerateTarget(FileContents* source_file, printf("failed to back up source file\n"); return 1; } made_copy = 1; made_copy = true; retry = 0; } else { int enough_space = 0; bool enough_space = false; if (retry > 0) { size_t free_space = FreeSpaceForFile(target_fs.c_str()); enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum (free_space > (target_size * 3 / 2)); // 50% margin of error if (!enough_space) { printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n", target_size, free_space, retry, enough_space); printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n", target_size, free_space, retry, enough_space); } } Loading @@ -723,7 +717,7 @@ static int GenerateTarget(FileContents* source_file, retry = 0; } if (!enough_space && source_patch_value != NULL) { if (!enough_space && source_patch_value != nullptr) { // Using the original source, but not enough free space. First // copy the source file to cache, then delete it from the original // location. Loading @@ -745,7 +739,7 @@ static int GenerateTarget(FileContents* source_file, printf("failed to back up source file\n"); return 1; } made_copy = 1; made_copy = true; unlink(source_filename); size_t free_space = FreeSpaceForFile(target_fs.c_str()); Loading @@ -753,48 +747,43 @@ static int GenerateTarget(FileContents* source_file, } } SinkFn sink = NULL; void* token = NULL; int output_fd = -1; SinkFn sink = nullptr; void* token = nullptr; unique_fd output_fd; if (target_is_partition) { // We store the decoded output in memory. sink = MemorySink; token = &memory_sink_str; } else { // We write the decoded output to "<tgt-file>.patch". output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); if (output_fd < 0) { printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), strerror(errno)); output_fd.reset(ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR)); if (output_fd == -1) { printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } sink = FileSink; token = &output_fd; } SHA1_Init(&ctx); int result; if (use_bsdiff) { result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(), patch, 0, sink, token, &ctx); result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(), patch, 0, sink, token, &ctx); } else { result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(), patch, sink, token, &ctx, bonus_data); result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(), patch, sink, token, &ctx, bonus_data); } if (!target_is_partition) { if (ota_fsync(output_fd) != 0) { printf("failed to fsync file \"%s\" (%s)\n", tmp_target_filename.c_str(), strerror(errno)); printf("failed to fsync file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno)); result = 1; } if (ota_close(output_fd) != 0) { printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(), strerror(errno)); printf("failed to close file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno)); result = 1; } } Loading @@ -802,7 +791,7 @@ static int GenerateTarget(FileContents* source_file, if (result != 0) { if (retry == 0) { printf("applying patch failed\n"); return result != 0; return 1; } else { printf("applying patch failed; retrying\n"); } Loading Loading @@ -832,13 +821,13 @@ static int GenerateTarget(FileContents* source_file, return 1; } } else { // Give the .patch file the same owner, group, and mode of the // original source file. // Give the .patch file the same owner, group, and mode of the original source file. if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) { printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) { if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) { printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno)); return 1; } Loading @@ -850,8 +839,7 @@ static int GenerateTarget(FileContents* source_file, } } // If this run of applypatch created the copy, and we're here, we // can delete it. // If this run of applypatch created the copy, and we're here, we can delete it. if (made_copy) { unlink(CACHE_TEMP_SOURCE); } Loading
otafault/ota_io.h +10 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <stdio.h> #include <sys/stat.h> #include <android-base/unique_fd.h> #define OTAIO_CACHE_FNAME "/cache/saved.file" void ota_set_fault_files(); Loading @@ -50,4 +52,12 @@ ssize_t ota_write(int fd, const void* buf, size_t nbyte); int ota_fsync(int fd); struct OtaCloser { static void Close(int fd) { ota_close(fd); } }; using unique_fd = android::base::unique_fd_impl<OtaCloser>; #endif