Add Updater class and remove UpdaterInfo
The UpdaterInfo class is merely a collection of pointers and POD types.
We can replace it with a Updater class that has the ownership of the
resources. This also makes this class extensible as we plan to add more
functionality in the host simulator.
Bug: 131911365
Test: unit tests pass, run an update on cuttlefish and check last_install
Change-Id: I07ca5963bbee8ae3cb85ccc184464910aa73d4e4
diff --git a/updater/Android.bp b/updater/Android.bp
index b80cdb3..daf7e32 100644
--- a/updater/Android.bp
+++ b/updater/Android.bp
@@ -70,6 +70,7 @@
"commands.cpp",
"dynamic_partitions.cpp",
"install.cpp",
+ "updater.cpp",
],
include_dirs: [
diff --git a/updater/Android.mk b/updater/Android.mk
index c7a6ba9..0178239 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -59,7 +59,7 @@
LOCAL_MODULE := updater
LOCAL_SRC_FILES := \
- updater.cpp
+ updater_main.cpp
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index b008c28..3089865 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -42,6 +42,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <applypatch/applypatch.h>
@@ -1668,15 +1669,9 @@
return StringValue("");
}
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
- if (ui == nullptr) {
- return StringValue("");
- }
-
- FILE* cmd_pipe = ui->cmd_pipe;
- ZipArchiveHandle za = ui->package_zip;
-
- if (cmd_pipe == nullptr || za == nullptr) {
+ auto updater = static_cast<Updater*>(state->cookie);
+ ZipArchiveHandle za = updater->package_handle();
+ if (za == nullptr) {
return StringValue("");
}
@@ -1686,8 +1681,8 @@
LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package";
return StringValue("");
}
+ params.patch_start = updater->GetMappedPackageAddress() + patch_entry.offset;
- params.patch_start = ui->package_zip_addr + patch_entry.offset;
std::string_view new_data(new_data_fn->data);
ZipEntry new_entry;
if (FindEntry(za, new_data, &new_entry) != 0) {
@@ -1887,8 +1882,10 @@
LOG(WARNING) << "Failed to update the last command file.";
}
- fprintf(cmd_pipe, "set_progress %.4f\n", static_cast<double>(params.written) / total_blocks);
- fflush(cmd_pipe);
+ updater->WriteToCommandPipe(
+ android::base::StringPrintf("set_progress %.4f",
+ static_cast<double>(params.written) / total_blocks),
+ true);
}
}
@@ -1915,11 +1912,13 @@
const char* partition = strrchr(blockdev_filename->data.c_str(), '/');
if (partition != nullptr && *(partition + 1) != 0) {
- fprintf(cmd_pipe, "log bytes_written_%s: %" PRIu64 "\n", partition + 1,
- static_cast<uint64_t>(params.written) * BLOCKSIZE);
- fprintf(cmd_pipe, "log bytes_stashed_%s: %" PRIu64 "\n", partition + 1,
- static_cast<uint64_t>(params.stashed) * BLOCKSIZE);
- fflush(cmd_pipe);
+ updater->WriteToCommandPipe(
+ android::base::StringPrintf("log bytes_written_%s: %" PRIu64, partition + 1,
+ static_cast<uint64_t>(params.written) * BLOCKSIZE));
+ updater->WriteToCommandPipe(
+ android::base::StringPrintf("log bytes_stashed_%s: %" PRIu64, partition + 1,
+ static_cast<uint64_t>(params.stashed) * BLOCKSIZE),
+ true);
}
// Delete stash only after successfully completing the update, as it may contain blocks needed
// to complete the update later.
@@ -2172,8 +2171,11 @@
uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]);
if (mount_count > 0) {
- uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count);
- uiPrintf(state, "Last remount happened on %s", ctime(&mount_time));
+ auto updater = static_cast<Updater*>(state->cookie);
+ updater->UiPrint(
+ android::base::StringPrintf("Device was remounted R/W %" PRIu16 " times", mount_count));
+ updater->UiPrint(
+ android::base::StringPrintf("Last remount happened on %s", ctime(&mount_time)));
}
return StringValue("t");
diff --git a/updater/include/updater/install.h b/updater/include/updater/install.h
index 8d6ca47..9fe2031 100644
--- a/updater/include/updater/install.h
+++ b/updater/include/updater/install.h
@@ -14,15 +14,6 @@
* limitations under the License.
*/
-#ifndef _UPDATER_INSTALL_H_
-#define _UPDATER_INSTALL_H_
-
-struct State;
+#pragma once
void RegisterInstallFunctions();
-
-// uiPrintf function prints msg to screen as well as logs
-void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...)
- __attribute__((__format__(printf, 2, 3)));
-
-#endif
diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h
index f4a2fe8..d546829 100644
--- a/updater/include/updater/updater.h
+++ b/updater/include/updater/updater.h
@@ -14,22 +14,75 @@
* limitations under the License.
*/
-#ifndef _UPDATER_UPDATER_H_
-#define _UPDATER_UPDATER_H_
+#pragma once
+#include <stdint.h>
#include <stdio.h>
+
+#include <memory>
+#include <string>
+
#include <ziparchive/zip_archive.h>
-typedef struct {
- FILE* cmd_pipe;
- ZipArchiveHandle package_zip;
- int version;
-
- uint8_t* package_zip_addr;
- size_t package_zip_len;
-} UpdaterInfo;
+#include "edify/expr.h"
+#include "otautil/error_code.h"
+#include "otautil/sysutil.h"
struct selabel_handle;
-extern struct selabel_handle *sehandle;
-#endif
+class Updater {
+ public:
+ ~Updater();
+
+ // Memory-maps the OTA package and opens it as a zip file. Also sets up the command pipe and
+ // selabel handle. TODO(xunchang) implement a run time environment class and move sehandle there.
+ bool Init(int fd, const std::string& package_filename, bool is_retry,
+ struct selabel_handle* sehandle);
+
+ // Parses and evaluates the updater-script in the OTA package. Reports the error code if the
+ // evaluation fails.
+ bool RunUpdate();
+
+ // Writes the message to command pipe, adds a new line in the end.
+ void WriteToCommandPipe(const std::string& message, bool flush = false) const;
+
+ // Sends over the message to recovery to print it on the screen.
+ void UiPrint(const std::string& message) const;
+
+ ZipArchiveHandle package_handle() const {
+ return package_handle_;
+ }
+ struct selabel_handle* sehandle() const {
+ return sehandle_;
+ }
+ std::string result() const {
+ return result_;
+ }
+
+ uint8_t* GetMappedPackageAddress() const {
+ return mapped_package_.addr;
+ }
+
+ private:
+ friend class UpdaterTestBase;
+ friend class UpdaterTest;
+ // Where in the package we expect to find the edify script to execute.
+ // (Note it's "updateR-script", not the older "update-script".)
+ static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script";
+
+ // Reads the entry |name| in the zip archive and put the result in |content|.
+ bool ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name, std::string* content);
+
+ // Parses the error code embedded in state->errmsg; and reports the error code and cause code.
+ void ParseAndReportErrorCode(State* state);
+
+ MemMapping mapped_package_;
+ ZipArchiveHandle package_handle_{ nullptr };
+ std::string updater_script_;
+
+ bool is_retry_{ false };
+ std::unique_ptr<FILE, decltype(&fclose)> cmd_pipe_{ nullptr, fclose };
+ struct selabel_handle* sehandle_{ nullptr };
+
+ std::string result_;
+};
diff --git a/updater/install.cpp b/updater/install.cpp
index c30f639..b4d8840 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -64,36 +64,6 @@
#include "otautil/sysutil.h"
#include "updater/updater.h"
-// Send over the buffer to recovery though the command pipe.
-static void uiPrint(State* state, const std::string& buffer) {
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
-
- // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "".
- // So skip sending empty strings to UI.
- std::vector<std::string> lines = android::base::Split(buffer, "\n");
- for (auto& line : lines) {
- if (!line.empty()) {
- fprintf(ui->cmd_pipe, "ui_print %s\n", line.c_str());
- }
- }
-
- // On the updater side, we need to dump the contents to stderr (which has
- // been redirected to the log file). Because the recovery will only print
- // the contents to screen when processing pipe command ui_print.
- LOG(INFO) << buffer;
-}
-
-void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) {
- std::string error_msg;
-
- va_list ap;
- va_start(ap, format);
- android::base::StringAppendV(&error_msg, format, ap);
- va_end(ap);
-
- 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<std::unique_ptr<Expr>>& argv) {
@@ -103,7 +73,7 @@
}
std::string buffer = android::base::Join(args, "");
- uiPrint(state, buffer);
+ static_cast<Updater*>(state->cookie)->UiPrint(buffer);
return StringValue(buffer);
}
@@ -129,7 +99,7 @@
const std::string& zip_path = args[0];
const std::string& dest_path = args[1];
- ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+ ZipArchiveHandle za = static_cast<Updater*>(state->cookie)->package_handle();
ZipEntry entry;
if (FindEntry(za, zip_path, &entry) != 0) {
LOG(ERROR) << name << ": no " << zip_path << " in package";
@@ -172,7 +142,7 @@
}
const std::string& zip_path = args[0];
- ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+ ZipArchiveHandle za = static_cast<Updater*>(state->cookie)->package_handle();
ZipEntry entry;
if (FindEntry(za, zip_path, &entry) != 0) {
return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name,
@@ -311,11 +281,11 @@
name);
}
+ auto updater = static_cast<Updater*>(state->cookie);
{
char* secontext = nullptr;
-
- if (sehandle) {
- selabel_lookup(sehandle, &secontext, mount_point.c_str(), 0755);
+ if (updater->sehandle()) {
+ selabel_lookup(updater->sehandle(), &secontext, mount_point.c_str(), 0755);
setfscreatecon(secontext);
}
@@ -329,8 +299,9 @@
if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(),
MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) {
- uiPrintf(state, "%s: Failed to mount %s at %s: %s", name, location.c_str(), mount_point.c_str(),
- strerror(errno));
+ updater->UiPrint(android::base::StringPrintf("%s: Failed to mount %s at %s: %s", name,
+ location.c_str(), mount_point.c_str(),
+ strerror(errno)));
return StringValue("");
}
@@ -376,15 +347,18 @@
"mount_point argument to unmount() can't be empty");
}
+ auto updater = static_cast<Updater*>(state->cookie);
scan_mounted_volumes();
MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str());
if (vol == nullptr) {
- uiPrintf(state, "Failed to unmount %s: No such volume", mount_point.c_str());
+ updater->UiPrint(
+ android::base::StringPrintf("Failed to unmount %s: No such volume", mount_point.c_str()));
return nullptr;
} else {
int ret = unmount_mounted_volume(vol);
if (ret != 0) {
- uiPrintf(state, "Failed to unmount %s: %s", mount_point.c_str(), strerror(errno));
+ updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(),
+ strerror(errno)));
}
}
@@ -529,8 +503,8 @@
sec_str.c_str());
}
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
- fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
+ auto updater = static_cast<Updater*>(state->cookie);
+ updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec));
return StringValue(frac_str);
}
@@ -553,8 +527,8 @@
frac_str.c_str());
}
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
- fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
+ auto updater = static_cast<Updater*>(state->cookie);
+ updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac));
return StringValue(frac_str);
}
@@ -653,7 +627,8 @@
return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
argv.size());
}
- fprintf(static_cast<UpdaterInfo*>(state->cookie)->cmd_pipe, "wipe_cache\n");
+
+ static_cast<Updater*>(state->cookie)->WriteToCommandPipe("wipe_cache");
return StringValue("t");
}
@@ -881,8 +856,7 @@
return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
argv.size());
}
- UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
- fprintf(ui->cmd_pipe, "enable_reboot\n");
+ static_cast<Updater*>(state->cookie)->WriteToCommandPipe("enable_reboot");
return StringValue("t");
}
diff --git a/updater/updater.cpp b/updater/updater.cpp
index a020699..e0679fb 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -16,8 +16,6 @@
#include "updater/updater.h"
-#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -25,197 +23,155 @@
#include <android-base/logging.h>
#include <android-base/strings.h>
-#include <selinux/android.h>
-#include <selinux/label.h>
-#include <selinux/selinux.h>
-#include <ziparchive/zip_archive.h>
-#include "edify/expr.h"
-#include "otautil/dirutil.h"
-#include "otautil/error_code.h"
-#include "otautil/sysutil.h"
-#include "updater/blockimg.h"
-#include "updater/dynamic_partitions.h"
-#include "updater/install.h"
-
-// Generated by the makefile, this function defines the
-// RegisterDeviceExtensions() function, which calls all the
-// registration functions for device-specific extensions.
-#include "register.inc"
-
-// Where in the package we expect to find the edify script to execute.
-// (Note it's "updateR-script", not the older "update-script".)
-static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script";
-
-struct selabel_handle *sehandle;
-
-static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
- const char* /* tag */, const char* /* file */, unsigned int /* line */,
- const char* message) {
- fprintf(stdout, "%s\n", message);
+Updater::~Updater() {
+ if (package_handle_) {
+ CloseArchive(package_handle_);
+ }
}
-int main(int argc, char** argv) {
- // Various things log information to stdout or stderr more or less
- // at random (though we've tried to standardize on stdout). The
- // log file makes more sense if buffering is turned off so things
- // appear in the right order.
- setbuf(stdout, nullptr);
- setbuf(stderr, nullptr);
-
- // We don't have logcat yet under recovery. Update logs will always be written to stdout
- // (which is redirected to recovery.log).
- android::base::InitLogging(argv, &UpdaterLogger);
-
- if (argc != 4 && argc != 5) {
- LOG(ERROR) << "unexpected number of arguments: " << argc;
- return 1;
- }
-
- char* version = argv[1];
- if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
- // We support version 1, 2, or 3.
- LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
- return 2;
- }
-
+bool Updater::Init(int fd, const std::string& package_filename, bool is_retry,
+ struct selabel_handle* sehandle) {
// Set up the pipe for sending commands back to the parent process.
-
- int fd = atoi(argv[2]);
- FILE* cmd_pipe = fdopen(fd, "wb");
- setlinebuf(cmd_pipe);
-
- // Extract the script from the package.
-
- const char* package_filename = argv[3];
- MemMapping map;
- if (!map.MapFile(package_filename)) {
- LOG(ERROR) << "failed to map package " << argv[3];
- return 3;
- }
- ZipArchiveHandle za;
- int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za);
- if (open_err != 0) {
- LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err);
- CloseArchive(za);
- return 3;
+ cmd_pipe_.reset(fdopen(fd, "wb"));
+ if (!cmd_pipe_) {
+ LOG(ERROR) << "Failed to open the command pipe";
+ return false;
}
- ZipEntry script_entry;
- int find_err = FindEntry(za, SCRIPT_NAME, &script_entry);
- if (find_err != 0) {
- LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": "
- << ErrorCodeString(find_err);
- CloseArchive(za);
- return 4;
+ setlinebuf(cmd_pipe_.get());
+
+ if (!mapped_package_.MapFile(package_filename)) {
+ LOG(ERROR) << "failed to map package " << package_filename;
+ return false;
+ }
+ if (int open_err = OpenArchiveFromMemory(mapped_package_.addr, mapped_package_.length,
+ package_filename.c_str(), &package_handle_);
+ open_err != 0) {
+ LOG(ERROR) << "failed to open package " << package_filename << ": "
+ << ErrorCodeString(open_err);
+ return false;
+ }
+ if (!ReadEntryToString(package_handle_, SCRIPT_NAME, &updater_script_)) {
+ return false;
}
- std::string script;
- script.resize(script_entry.uncompressed_length);
- int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast<uint8_t*>(&script[0]),
- script_entry.uncompressed_length);
- if (extract_err != 0) {
- LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err);
- CloseArchive(za);
- return 5;
+ is_retry_ = is_retry;
+
+ sehandle_ = sehandle;
+ if (!sehandle_) {
+ fprintf(cmd_pipe_.get(), "ui_print Warning: No file_contexts\n");
}
+ return true;
+}
- // Configure edify's functions.
-
- RegisterBuiltins();
- RegisterInstallFunctions();
- RegisterBlockImageFunctions();
- RegisterDynamicPartitionsFunctions();
- RegisterDeviceExtensions();
-
+bool Updater::RunUpdate() {
// Parse the script.
-
std::unique_ptr<Expr> root;
int error_count = 0;
- int error = ParseString(script, &root, &error_count);
+ int error = ParseString(updater_script_, &root, &error_count);
if (error != 0 || error_count > 0) {
LOG(ERROR) << error_count << " parse errors";
- CloseArchive(za);
- return 6;
- }
-
- sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
-
- if (!sehandle) {
- fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
+ return false;
}
// Evaluate the parsed script.
+ State state(updater_script_, this);
+ state.is_retry = is_retry_;
- UpdaterInfo updater_info;
- updater_info.cmd_pipe = cmd_pipe;
- updater_info.package_zip = za;
- updater_info.version = atoi(version);
- updater_info.package_zip_addr = map.addr;
- updater_info.package_zip_len = map.length;
+ bool status = Evaluate(&state, root, &result_);
+ if (status) {
+ fprintf(cmd_pipe_.get(), "ui_print script succeeded: result was [%s]\n", result_.c_str());
+ // Even though the script doesn't abort, still log the cause code if result is empty.
+ if (result_.empty() && state.cause_code != kNoCause) {
+ fprintf(cmd_pipe_.get(), "log cause: %d\n", state.cause_code);
+ }
+ return true;
+ }
- State state(script, &updater_info);
+ ParseAndReportErrorCode(&state);
+ return false;
+}
- if (argc == 5) {
- if (strcmp(argv[4], "retry") == 0) {
- state.is_retry = true;
- } else {
- printf("unexpected argument: %s", argv[4]);
+void Updater::WriteToCommandPipe(const std::string& message, bool flush) const {
+ fprintf(cmd_pipe_.get(), "%s\n", message.c_str());
+ if (flush) {
+ fflush(cmd_pipe_.get());
+ }
+}
+
+void Updater::UiPrint(const std::string& message) const {
+ // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "".
+ // so skip sending empty strings to ui.
+ std::vector<std::string> lines = android::base::Split(message, "\n");
+ for (const auto& line : lines) {
+ if (!line.empty()) {
+ fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str());
}
}
- std::string result;
- bool status = Evaluate(&state, root, &result);
+ // on the updater side, we need to dump the contents to stderr (which has
+ // been redirected to the log file). because the recovery will only print
+ // the contents to screen when processing pipe command ui_print.
+ LOG(INFO) << message;
+}
- if (!status) {
- if (state.errmsg.empty()) {
- LOG(ERROR) << "script aborted (no error message)";
- fprintf(cmd_pipe, "ui_print script aborted (no error message)\n");
- } else {
- LOG(ERROR) << "script aborted: " << state.errmsg;
- const std::vector<std::string> lines = android::base::Split(state.errmsg, "\n");
- for (const std::string& line : lines) {
- // Parse the error code in abort message.
- // Example: "E30: This package is for bullhead devices."
- if (!line.empty() && line[0] == 'E') {
- if (sscanf(line.c_str(), "E%d: ", &state.error_code) != 1) {
- LOG(ERROR) << "Failed to parse error code: [" << line << "]";
- }
- }
- fprintf(cmd_pipe, "ui_print %s\n", line.c_str());
- }
- }
-
- // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
- // a more specific code has been set in errmsg.
- if (state.error_code == kNoError) {
- state.error_code = kScriptExecutionFailure;
- }
- fprintf(cmd_pipe, "log error: %d\n", state.error_code);
- // Cause code should provide additional information about the abort.
- if (state.cause_code != kNoCause) {
- fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
- if (state.cause_code == kPatchApplicationFailure) {
- LOG(INFO) << "Patch application failed, retry update.";
- fprintf(cmd_pipe, "retry_update\n");
- } else if (state.cause_code == kEioFailure) {
- LOG(INFO) << "Update failed due to EIO, retry update.";
- fprintf(cmd_pipe, "retry_update\n");
- }
- }
-
- if (updater_info.package_zip) {
- CloseArchive(updater_info.package_zip);
- }
- return 7;
+void Updater::ParseAndReportErrorCode(State* state) {
+ CHECK(state);
+ if (state->errmsg.empty()) {
+ LOG(ERROR) << "script aborted (no error message)";
+ fprintf(cmd_pipe_.get(), "ui_print script aborted (no error message)\n");
} else {
- fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str());
+ LOG(ERROR) << "script aborted: " << state->errmsg;
+ const std::vector<std::string> lines = android::base::Split(state->errmsg, "\n");
+ for (const std::string& line : lines) {
+ // Parse the error code in abort message.
+ // Example: "E30: This package is for bullhead devices."
+ if (!line.empty() && line[0] == 'E') {
+ if (sscanf(line.c_str(), "E%d: ", &state->error_code) != 1) {
+ LOG(ERROR) << "Failed to parse error code: [" << line << "]";
+ }
+ }
+ fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str());
+ }
}
- if (updater_info.package_zip) {
- CloseArchive(updater_info.package_zip);
+ // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
+ // a more specific code has been set in errmsg.
+ if (state->error_code == kNoError) {
+ state->error_code = kScriptExecutionFailure;
+ }
+ fprintf(cmd_pipe_.get(), "log error: %d\n", state->error_code);
+ // Cause code should provide additional information about the abort.
+ if (state->cause_code != kNoCause) {
+ fprintf(cmd_pipe_.get(), "log cause: %d\n", state->cause_code);
+ if (state->cause_code == kPatchApplicationFailure) {
+ LOG(INFO) << "Patch application failed, retry update.";
+ fprintf(cmd_pipe_.get(), "retry_update\n");
+ } else if (state->cause_code == kEioFailure) {
+ LOG(INFO) << "Update failed due to EIO, retry update.";
+ fprintf(cmd_pipe_.get(), "retry_update\n");
+ }
+ }
+}
+
+bool Updater::ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name,
+ std::string* content) {
+ ZipEntry entry;
+ int find_err = FindEntry(za, entry_name, &entry);
+ if (find_err != 0) {
+ LOG(ERROR) << "failed to find " << entry_name
+ << " in the package: " << ErrorCodeString(find_err);
+ return false;
}
- return 0;
+ content->resize(entry.uncompressed_length);
+ int extract_err = ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&content->at(0)),
+ entry.uncompressed_length);
+ if (extract_err != 0) {
+ LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err);
+ return false;
+ }
+
+ return true;
}
diff --git a/updater/updater_main.cpp b/updater/updater_main.cpp
new file mode 100644
index 0000000..dd22c13
--- /dev/null
+++ b/updater/updater_main.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+#include "edify/expr.h"
+#include "updater/blockimg.h"
+#include "updater/dynamic_partitions.h"
+#include "updater/install.h"
+#include "updater/updater.h"
+
+// Generated by the makefile, this function defines the
+// RegisterDeviceExtensions() function, which calls all the
+// registration functions for device-specific extensions.
+#include "register.inc"
+
+static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
+ const char* /* tag */, const char* /* file */, unsigned int /* line */,
+ const char* message) {
+ fprintf(stdout, "%s\n", message);
+}
+
+int main(int argc, char** argv) {
+ // Various things log information to stdout or stderr more or less
+ // at random (though we've tried to standardize on stdout). The
+ // log file makes more sense if buffering is turned off so things
+ // appear in the right order.
+ setbuf(stdout, nullptr);
+ setbuf(stderr, nullptr);
+
+ // We don't have logcat yet under recovery. Update logs will always be written to stdout
+ // (which is redirected to recovery.log).
+ android::base::InitLogging(argv, &UpdaterLogger);
+
+ if (argc != 4 && argc != 5) {
+ LOG(ERROR) << "unexpected number of arguments: " << argc;
+ return 1;
+ }
+
+ char* version = argv[1];
+ if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
+ // We support version 1, 2, or 3.
+ LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
+ return 1;
+ }
+
+ int fd;
+ if (!android::base::ParseInt(argv[2], &fd)) {
+ LOG(ERROR) << "Failed to parse fd in " << argv[2];
+ return 1;
+ }
+
+ std::string package_name = argv[3];
+
+ bool is_retry = false;
+ if (argc == 5) {
+ if (strcmp(argv[4], "retry") == 0) {
+ is_retry = true;
+ } else {
+ LOG(ERROR) << "unexpected argument: " << argv[4];
+ return 1;
+ }
+ }
+
+ // Configure edify's functions.
+ RegisterBuiltins();
+ RegisterInstallFunctions();
+ RegisterBlockImageFunctions();
+ RegisterDynamicPartitionsFunctions();
+ RegisterDeviceExtensions();
+
+ auto sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+
+ Updater updater;
+ if (!updater.Init(fd, package_name, is_retry, sehandle)) {
+ return 1;
+ }
+
+ if (!updater.RunUpdate()) {
+ return 1;
+ }
+
+ return 0;
+}
\ No newline at end of file