summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--aidl/android/gsi/IGsiService.aidl24
-rw-r--r--file_paths.h1
-rw-r--r--gsi_service.cpp87
-rw-r--r--gsi_service.h8
-rw-r--r--gsi_tool.cpp28
-rw-r--r--libgsi.cpp10
6 files changed, 126 insertions, 32 deletions
diff --git a/aidl/android/gsi/IGsiService.aidl b/aidl/android/gsi/IGsiService.aidl
index 57028fb..7cd8408 100644
--- a/aidl/android/gsi/IGsiService.aidl
+++ b/aidl/android/gsi/IGsiService.aidl
@@ -83,9 +83,11 @@ interface IGsiService {
* Complete a GSI installation and mark it as bootable. The caller is
* responsible for rebooting the device as soon as possible.
*
+ * @param oneShot If true, the GSI will boot once and then disable itself.
+ * It can still be re-enabled again later with setGsiBootable.
* @return INSTALL_* error code.
*/
- int setGsiBootable();
+ int setGsiBootable(boolean oneShot);
/**
* Cancel an in-progress GSI install.
@@ -127,4 +129,24 @@ interface IGsiService {
* Returns true if a gsi is installed.
*/
boolean isGsiInstalled();
+
+ /* No GSI is installed. */
+ const int BOOT_STATUS_NOT_INSTALLED = 0;
+ /* GSI is installed, but booting is disabled. */
+ const int BOOT_STATUS_DISABLED = 1;
+ /* GSI is installed, but will only boot once. */
+ const int BOOT_STATUS_SINGLE_BOOT = 2;
+ /* GSI is installed and bootable. */
+ const int BOOT_STATUS_ENABLED = 3;
+ /* GSI will be wiped next boot. */
+ const int BOOT_STATUS_WILL_WIPE = 4;
+
+ /**
+ * Returns the boot status of a GSI. See the BOOT_STATUS constants in IGsiService.
+ *
+ * GSI_STATE_NOT_INSTALLED will be returned if no GSI installation has been
+ * fully completed. Any other value indicates a GSI is installed. If a GSI
+ * currently running, DISABLED or SINGLE_BOOT can still be returned.
+ */
+ int getGsiBootStatus();
}
diff --git a/file_paths.h b/file_paths.h
index 0005b19..0fc255e 100644
--- a/file_paths.h
+++ b/file_paths.h
@@ -26,6 +26,7 @@ static constexpr char kSystemFile[] = "/data/gsi/system_gsi.img";
static constexpr char kGsiMetadataFolder[] = "/metadata/gsi";
static constexpr char kGsiLpMetadataFile[] = "/metadata/gsi/lp_metadata";
+static constexpr char kGsiOneShotBootFile[] = "/metadata/gsi/one_shot_boot";
// This file can contain the following values:
// [int] - boot attempt counter, starting from 0
diff --git a/gsi_service.cpp b/gsi_service.cpp
index 5b975ac..bf5410e 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -163,15 +163,15 @@ binder::Status GsiService::commitGsiChunkFromMemory(const std::vector<uint8_t>&
return binder::Status::ok();
}
-binder::Status GsiService::setGsiBootable(int* _aidl_return) {
+binder::Status GsiService::setGsiBootable(bool one_shot, int* _aidl_return) {
std::lock_guard<std::mutex> guard(main_lock_);
if (installing_) {
ENFORCE_SYSTEM;
- *_aidl_return = SetGsiBootable() ? INSTALL_OK : INSTALL_ERROR_GENERIC;
+ *_aidl_return = SetGsiBootable(one_shot);
} else {
ENFORCE_SYSTEM_OR_SHELL;
- *_aidl_return = ReenableGsi();
+ *_aidl_return = ReenableGsi(one_shot);
}
return binder::Status::ok();
}
@@ -241,6 +241,45 @@ binder::Status GsiService::cancelGsiInstall(bool* _aidl_return) {
return binder::Status::ok();
}
+binder::Status GsiService::getGsiBootStatus(int* _aidl_return) {
+ ENFORCE_SYSTEM_OR_SHELL;
+ std::lock_guard<std::mutex> guard(main_lock_);
+
+ if (!IsGsiInstalled()) {
+ *_aidl_return = BOOT_STATUS_NOT_INSTALLED;
+ return binder::Status::ok();
+ }
+
+ std::string boot_key;
+ if (!GetInstallStatus(&boot_key)) {
+ PLOG(ERROR) << "read " << kGsiInstallStatusFile;
+ *_aidl_return = BOOT_STATUS_NOT_INSTALLED;
+ return binder::Status::ok();
+ }
+
+ bool single_boot = !access(kGsiOneShotBootFile, F_OK);
+
+ if (boot_key == kInstallStatusWipe) {
+ // This overrides all other statuses.
+ *_aidl_return = BOOT_STATUS_WILL_WIPE;
+ } else if (boot_key == kInstallStatusDisabled) {
+ // A single-boot GSI will have a "disabled" status, because it's
+ // disabled immediately upon reading the one_shot_boot file. However,
+ // we still want to return SINGLE_BOOT, because it makes the
+ // transition clearer to the user.
+ if (IsGsiRunning() && single_boot) {
+ *_aidl_return = BOOT_STATUS_SINGLE_BOOT;
+ } else {
+ *_aidl_return = BOOT_STATUS_DISABLED;
+ }
+ } else if (single_boot) {
+ *_aidl_return = BOOT_STATUS_SINGLE_BOOT;
+ } else {
+ *_aidl_return = BOOT_STATUS_ENABLED;
+ }
+ return binder::Status::ok();
+}
+
binder::Status GsiService::getUserdataImageSize(int64_t* _aidl_return) {
ENFORCE_SYSTEM;
*_aidl_return = -1;
@@ -605,33 +644,33 @@ bool GsiService::CommitGsiChunk(const void* data, size_t bytes) {
return true;
}
-bool GsiService::SetGsiBootable() {
+int GsiService::SetGsiBootable(bool one_shot) {
if (gsi_bytes_written_ != gsi_size_) {
// We cannot boot if the image is incomplete.
LOG(ERROR) << "image incomplete; expected " << gsi_size_ << " bytes, waiting for "
<< (gsi_size_ - gsi_bytes_written_) << " bytes";
- return false;
+ return INSTALL_ERROR_GENERIC;
}
if (fsync(system_fd_)) {
PLOG(ERROR) << "fsync failed";
- return false;
+ return INSTALL_ERROR_GENERIC;
}
// If these files moved, the metadata file will be invalid.
- if (!CheckPinning(kUserdataFile) || !CheckPinning(kSystemFile)) {
- return false;
+ if (!CheckPinning(kUserdataFile) || !CheckPinning(kSystemFile) || !SetBootMode(one_shot)) {
+ return INSTALL_ERROR_GENERIC;
}
if (!CreateMetadataFile(*metadata_.get()) || !CreateInstallStatusFile()) {
- return false;
+ return INSTALL_ERROR_GENERIC;
}
PostInstallCleanup();
- return true;
+ return INSTALL_OK;
}
-int GsiService::ReenableGsi() {
+int GsiService::ReenableGsi(bool one_shot) {
if (!android::gsi::IsGsiInstalled()) {
LOG(ERROR) << "no gsi installed - cannot re-enable";
return INSTALL_ERROR_GENERIC;
@@ -671,7 +710,8 @@ int GsiService::ReenableGsi() {
if (!metadata) {
return INSTALL_ERROR_GENERIC;
}
- if (!CreateMetadataFile(*metadata.get()) || !CreateInstallStatusFile()) {
+ if (!CreateMetadataFile(*metadata.get()) || !SetBootMode(one_shot) ||
+ !CreateInstallStatusFile()) {
return INSTALL_ERROR_GENERIC;
}
return INSTALL_OK;
@@ -725,6 +765,7 @@ bool GsiService::RemoveGsiFiles(bool wipeUserdata) {
kSystemFile,
kGsiInstallStatusFile,
kGsiLpMetadataFile,
+ kGsiOneShotBootFile,
};
if (wipeUserdata) {
files.emplace_back(kUserdataFile);
@@ -853,6 +894,22 @@ bool GsiService::AddPartitionFiemap(MetadataBuilder* builder, Partition* partiti
return true;
}
+bool GsiService::SetBootMode(bool one_shot) {
+ if (one_shot) {
+ if (!android::base::WriteStringToFile("1", kGsiOneShotBootFile)) {
+ PLOG(ERROR) << "write " << kGsiOneShotBootFile;
+ return false;
+ }
+ } else if (!access(kGsiOneShotBootFile, F_OK)) {
+ std::string error;
+ if (!android::base::RemoveFileIfExists(kGsiOneShotBootFile, &error)) {
+ LOG(ERROR) << error;
+ return false;
+ }
+ }
+ return true;
+}
+
bool GsiService::CreateInstallStatusFile() {
if (!android::base::WriteStringToFile("0", kGsiInstallStatusFile)) {
PLOG(ERROR) << "write " << kGsiInstallStatusFile;
@@ -878,9 +935,9 @@ void GsiService::RunStartupTasks() {
RemoveGsiFiles(true /* wipeUserdata */);
}
} else {
- // NB: When kOnlyAllowSingleBoot is true, init will write "disabled"
- // into the install_status file, which will cause GetBootAttempts to
- // return false. Thus, we won't write "ok" here.
+ // NB: When single-boot is enabled, init will write "disabled" into the
+ // install_status file, which will cause GetBootAttempts to return
+ // false. Thus, we won't write "ok" here.
int ignore;
if (GetBootAttempts(boot_key, &ignore)) {
// Mark the GSI as having successfully booted.
diff --git a/gsi_service.h b/gsi_service.h
index d2a87bd..1250b45 100644
--- a/gsi_service.h
+++ b/gsi_service.h
@@ -47,13 +47,14 @@ class GsiService : public BinderService<GsiService>, public BnGsiService {
binder::Status commitGsiChunkFromMemory(const ::std::vector<uint8_t>& bytes,
bool* _aidl_return) override;
binder::Status cancelGsiInstall(bool* _aidl_return) override;
- binder::Status setGsiBootable(int* _aidl_return) override;
+ binder::Status setGsiBootable(bool oneShot, int* _aidl_return) override;
binder::Status removeGsiInstall(bool* _aidl_return) override;
binder::Status disableGsiInstall(bool* _aidl_return) override;
binder::Status isGsiRunning(bool* _aidl_return) override;
binder::Status isGsiInstalled(bool* _aidl_return) override;
binder::Status isGsiInstallInProgress(bool* _aidl_return) override;
binder::Status getUserdataImageSize(int64_t* _aidl_return) override;
+ binder::Status getGsiBootStatus(int* _aidl_return) override;
static char const* getServiceName() { return kGsiServiceName; }
@@ -77,8 +78,8 @@ class GsiService : public BinderService<GsiService>, public BnGsiService {
bool FormatUserdata();
bool CommitGsiChunk(int stream_fd, int64_t bytes);
bool CommitGsiChunk(const void* data, size_t bytes);
- bool SetGsiBootable();
- int ReenableGsi();
+ int SetGsiBootable(bool one_shot);
+ int ReenableGsi(bool one_shot);
bool DisableGsiInstall();
bool AddPartitionFiemap(android::fs_mgr::MetadataBuilder* builder,
android::fs_mgr::Partition* partition, const Image& image);
@@ -87,6 +88,7 @@ class GsiService : public BinderService<GsiService>, public BnGsiService {
int* error);
bool CreateInstallStatusFile();
bool CreateMetadataFile(const android::fs_mgr::LpMetadata& metadata);
+ bool SetBootMode(bool one_shot);
void PostInstallCleanup();
void StartAsyncOperation(const std::string& step, int64_t total_bytes);
diff --git a/gsi_tool.cpp b/gsi_tool.cpp
index 94cca16..3403cd1 100644
--- a/gsi_tool.cpp
+++ b/gsi_tool.cpp
@@ -284,7 +284,7 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
progress.Finish();
- status = gsid->setGsiBootable(&error);
+ status = gsid->setGsiBootable(true, &error);
if (!status.isOk() || error != IGsiService::INSTALL_OK) {
std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n";
return EX_SOFTWARE;
@@ -343,10 +343,23 @@ static int Status(sp<IGsiService> gsid, int argc, char** /* argv */) {
return 0;
}
-static int Enable(sp<IGsiService> gsid, int argc, char** /* argv */) {
- if (argc > 1) {
- std::cerr << "Unrecognized arguments to enable." << std::endl;
- return EX_USAGE;
+static int Enable(sp<IGsiService> gsid, int argc, char** argv) {
+ bool one_shot = false;
+
+ struct option options[] = {
+ {"single-boot", no_argument, nullptr, 's'},
+ {nullptr, 0, nullptr, 0},
+ };
+ int rv, index;
+ while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
+ switch (rv) {
+ case 's':
+ one_shot = true;
+ break;
+ default:
+ std::cerr << "Unrecognized argument to enable\n";
+ return EX_USAGE;
+ }
}
bool installed = false;
@@ -364,7 +377,7 @@ static int Enable(sp<IGsiService> gsid, int argc, char** /* argv */) {
}
int error;
- auto status = gsid->setGsiBootable(&error);
+ auto status = gsid->setGsiBootable(one_shot, &error);
if (!status.isOk() || error != IGsiService::INSTALL_OK) {
std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n";
return EX_SOFTWARE;
@@ -404,7 +417,8 @@ static int usage(int /* argc */, char* argv[]) {
" %s <disable|install|wipe|status> [options]\n"
"\n"
" disable Disable the currently installed GSI.\n"
- " enable Enable a previously disabled GSI.\n"
+ " enable [-s, --single-boot]\n"
+ " Enable a previously disabled GSI.\n"
" install Install a new GSI. Specify the image size with\n"
" --gsi-size and the desired userdata size with\n"
" --userdata-size (the latter defaults to 8GiB)\n"
diff --git a/libgsi.cpp b/libgsi.cpp
index fdb147e..5a3c1e2 100644
--- a/libgsi.cpp
+++ b/libgsi.cpp
@@ -42,10 +42,6 @@ bool IsGsiInstalled() {
return !access(kGsiInstallStatusFile, F_OK);
}
-// If true, we only ever allow a single boot into GSI. Rebooting the device
-// will bring the device back to its normal system image.
-static constexpr bool kOnlyAllowSingleBoot = true;
-
static bool WriteAndSyncFile(const std::string& data, const std::string& file) {
unique_fd fd(open(file.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
if (fd < 0) {
@@ -78,9 +74,11 @@ static bool CanBootIntoGsi(std::string* error) {
}
std::string new_key;
- if (kOnlyAllowSingleBoot) {
+ if (!access(kGsiOneShotBootFile, F_OK)) {
// Mark the GSI as disabled. This only affects the next boot, not
- // the current boot.
+ // the current boot. Note that we leave the one_shot status behind.
+ // This is so IGsiService can still return GSI_STATE_SINGLE_BOOT
+ // while the GSI is running.
new_key = kInstallStatusDisabled;
} else {
new_key = std::to_string(attempts + 1);