aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Cody Schuffelen <schuffelen@google.com>2024-03-27 15:54:05 -0700
committerA. Cody Schuffelen <schuffelen@google.com>2024-03-28 09:24:58 -0700
commit05e0ed3819dbf4bdd06cb7f6ba879de8f096f3d4 (patch)
treeb55460319a108c08e00e803e3fa3893ada7cdf76
parentef86ba0fa6a7b609fb50489f85c3befa1e184c30 (diff)
downloadcuttlefish-05e0ed3819dbf4bdd06cb7f6ba879de8f096f3d4.tar.gz
Support guest-initiated terminal boot failure
- JobExecutor in GceService can produce VIRTUAL_DEVICE_BOOT_FAILURE if a blocking dependency takes too long. - kernel_log_monitor watches for VIRTUAL_DEVICE_BOOT_FAILURE and VIRTUAL_DEVICE_BOOT_PENDING messages - CvdBootStateMachine will initiate a launcher exit on seeing VIRTUAL_DEVICE_BOOT_FAILURE - This is controlled by the new `--fail_fast` flag, which defaults to `true - ServerLoopImpl gains a new "failure exit" request type. - Some refactoring to use `List` rather than `ArrayList` types in GceService code. - Update the `.clang-format` file to 4-space-indent java code. It's still somewhat messy that CvdBootStateMachine sends messages to the external unix socket API of SeverLoopImpl. Actually limiting the wait time for bluetooth will happen in the following CL, so that can be rolled back on its own. Test: m Bug: 331668246 Change-Id: I92850b219dc9098b2b3c5139b091324b6bee03a2
-rw-r--r--.clang-format5
-rw-r--r--guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/EventReporter.java7
-rw-r--r--guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceFuture.java12
-rw-r--r--guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobBase.java5
-rw-r--r--guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobExecutor.java65
-rw-r--r--host/commands/assemble_cvd/flags.cc7
-rw-r--r--host/commands/assemble_cvd/flags_defaults.h3
-rw-r--r--host/commands/kernel_log_monitor/kernel_log_server.cc16
-rw-r--r--host/commands/kernel_log_monitor/kernel_log_server.h3
-rw-r--r--host/commands/run_cvd/boot_state_machine.cc23
-rw-r--r--host/commands/run_cvd/server_loop_impl.cpp14
-rw-r--r--host/commands/start/main.cc1
-rw-r--r--host/libs/command_util/runner/defs.h1
-rw-r--r--host/libs/command_util/util.cc1
-rw-r--r--host/libs/config/config_constants.h1
-rw-r--r--host/libs/config/cuttlefish_config.h2
-rw-r--r--host/libs/config/cuttlefish_config_instance.cpp8
17 files changed, 127 insertions, 47 deletions
diff --git a/.clang-format b/.clang-format
index a03f8b344..3a54b274b 100644
--- a/.clang-format
+++ b/.clang-format
@@ -17,6 +17,9 @@
# See https://clang.llvm.org/docs/ClangFormatStyleOptions.html for the
# various options that can be configured and for the definitions of each
# of the below options.
-
+---
BasedOnStyle: Google
IncludeBlocks: Preserve
+---
+Language: Java
+IndentWidth: 4
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/EventReporter.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/EventReporter.java
index 5d9b02f34..24d9c49a0 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/EventReporter.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/EventReporter.java
@@ -78,12 +78,11 @@ public class EventReporter extends JobBase {
* If all jobs have completed, reports boot completed and stops.
*/
@Override
- public void onDependencyStraggling(ArrayList<GceFuture<?>> deps) {
- reportMessage(String.format("%s: %s", VIRTUAL_DEVICE_BOOT_PENDING,
- GceFuture.toString(deps)));
+ public void onDependencyStraggling(List<GceFuture<?>> deps) {
+ reportMessage(
+ String.format("%s: %s", VIRTUAL_DEVICE_BOOT_PENDING, GceFuture.toString(deps)));
}
-
/** Report successful boot completion.
*
* Issue message to serial port confirming successful boot completion and
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceFuture.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceFuture.java
index 055377de7..28c9eb9fd 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceFuture.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceFuture.java
@@ -16,13 +16,13 @@
package com.android.google.gce.gceservice;
import android.util.Log;
-import java.util.ArrayList;
+import com.google.common.util.concurrent.AbstractFuture;
+import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
-import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
-import com.google.common.util.concurrent.AbstractFuture;
+import java.util.concurrent.TimeoutException;
public class GceFuture<T> extends AbstractFuture<T> {
private static final String LOG_TAG = "GceFuture";
@@ -90,10 +90,12 @@ public class GceFuture<T> extends AbstractFuture<T> {
/** Convert list of GceFuture objects to string representation.
*/
- public static String toString(ArrayList<GceFuture<?>> futures) {
+ public static String toString(List<GceFuture<?>> futures) {
StringBuilder b = new StringBuilder();
for (GceFuture<?> dep : futures) {
- if (b.length() > 0) b.append(", ");
+ if (b.length() > 0) {
+ b.append(", ");
+ }
b.append(dep.getName());
}
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobBase.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobBase.java
index aceb70f51..0f891f57c 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobBase.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobBase.java
@@ -16,7 +16,7 @@
package com.android.google.gce.gceservice;
import android.util.Log;
-import java.util.ArrayList;
+import java.util.List;
public abstract class JobBase {
private final String mTag;
@@ -41,11 +41,10 @@ public abstract class JobBase {
* @param stragglingDependencies list of dependencies that have not completed
* in last 10 seconds.
*/
- public void onDependencyStraggling(ArrayList<GceFuture<?>> stragglingDependencies) {
+ public void onDependencyStraggling(List<GceFuture<?>> stragglingDependencies) {
Log.i(mTag, "Waiting for: " + GceFuture.toString(stragglingDependencies));
}
-
/** Invoked, when all dependencies are ready and job is clear to commence.
*
* Returns number of second before re-execution, or 0, if job is done.
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobExecutor.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobExecutor.java
index 1eed0065b..16caca1fb 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobExecutor.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/JobExecutor.java
@@ -17,15 +17,16 @@ package com.android.google.gce.gceservice;
import android.util.Log;
import com.android.google.gce.gceservice.JobBase;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.HashSet;
+import java.util.concurrent.TimeoutException;
public class JobExecutor {
private static final int THREAD_POOL_SIZE = 8;
@@ -39,6 +40,10 @@ public class JobExecutor {
mStartedJobs = new HashSet<String>();
}
+ public void schedule(JobBase job, GceFuture<?>... futures) {
+ schedule(job, -1, futures);
+ }
+
/** Schedule job for (periodic) execution.
*
* First execution is performed at the earliest possibility.
@@ -46,29 +51,23 @@ public class JobExecutor {
*
* Note: iteration time is |delaySeconds| + total time needed to execute job.
*/
- public void schedule(final JobBase job, final GceFuture<?>... futures) {
+ public void schedule(
+ final JobBase job, final int checks, final GceFuture<?>... futuresVarargs) {
mExecutor.schedule(new Runnable() {
- private boolean mDependenciesReady = false;
- private ArrayList<GceFuture<?>> mFutures = new ArrayList<GceFuture<?>>();
-
- {
- for (GceFuture<?> future: futures) {
- mFutures.add(future);
- }
- }
-
public void run() {
+ List<GceFuture<?>> futures = Arrays.asList(futuresVarargs);
+ boolean mDependenciesReady = false;
+ int attemptNum = 0;
if (!mDependenciesReady) {
- while (!mFutures.isEmpty()) {
+ while (!futures.isEmpty()) {
ArrayList<GceFuture<?>> stragglers = new ArrayList<GceFuture<?>>();
long endTime = System.currentTimeMillis() + ITERATION_PERIOD_MS;
- for (GceFuture<?> future : mFutures) {
+ for (GceFuture<?> future : futures) {
try {
// Wait for at most ITERATION_PERIOD_MS. Check all futures,
// collect only those that still failed to complete.
- future.get(
- Math.max(0, endTime - System.currentTimeMillis()),
- TimeUnit.MILLISECONDS);
+ future.get(Math.max(0, endTime - System.currentTimeMillis()),
+ TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
stragglers.add(future);
} catch (InterruptedException e) {
@@ -77,17 +76,27 @@ public class JobExecutor {
// give it another go in the second loop.
stragglers.add(future);
} catch (Exception e) {
- Log.e(LOG_TAG, String.format(
+ Log.e(LOG_TAG,
+ String.format(
"Could not start job %s.", job.getClass().getName()),
- e);
+ e);
job.onDependencyFailed(e);
return;
}
}
- mFutures = stragglers;
- if (!mFutures.isEmpty()) {
- job.onDependencyStraggling(mFutures);
+ futures = stragglers;
+ if (!futures.isEmpty()) {
+ if (checks > 0 && attemptNum >= checks) {
+ String message =
+ String.format("Dependencies not ready after %d checks: %s",
+ attemptNum, GceFuture.toString(futures));
+ job.onDependencyFailed(new IllegalStateException(message));
+ return;
+ } else {
+ job.onDependencyStraggling(futures);
+ }
+ attemptNum++;
}
}
mDependenciesReady = true;
@@ -105,8 +114,10 @@ public class JobExecutor {
mStartedJobs.remove(jobName);
}
} catch (Exception e) {
- Log.e(LOG_TAG, String.format("Job %s threw an exception and will not be re-scheduled.",
- job.getClass().getName()), e);
+ Log.e(LOG_TAG,
+ String.format("Job %s threw an exception and will not be re-scheduled.",
+ job.getClass().getName()),
+ e);
}
}
}, 0, TimeUnit.SECONDS);
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 3c78614c6..f8cd3f426 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -479,6 +479,10 @@ DEFINE_string(straced_host_executables, CF_DEFAULTS_STRACED_HOST_EXECUTABLES,
DEFINE_bool(enable_host_sandbox, CF_DEFAULTS_HOST_SANDBOX,
"Lock down host processes with sandbox2");
+DEFINE_vec(
+ fail_fast, CF_DEFAULTS_FAIL_FAST ? "true" : "false",
+ "Whether to exit when a heuristic predicts the boot will not complete");
+
DECLARE_string(assembly_dir);
DECLARE_string(boot_image);
DECLARE_string(system_image_dir);
@@ -1152,6 +1156,8 @@ Result<CuttlefishConfig> InitializeCuttlefishConfiguration(
std::vector<std::string> device_external_network_vec =
CF_EXPECT(GET_FLAG_STR_VALUE(device_external_network));
+ std::vector<bool> fail_fast_vec = CF_EXPECT(GET_FLAG_BOOL_VALUE(fail_fast));
+
std::vector<std::string> mcu_config_vec = CF_EXPECT(GET_FLAG_STR_VALUE(mcu_config_path));
std::string default_enable_sandbox = "";
@@ -1387,6 +1393,7 @@ Result<CuttlefishConfig> InitializeCuttlefishConfiguration(
instance.set_kgdb(console_vec[instance_index] && kgdb_vec[instance_index]);
instance.set_blank_data_image_mb(blank_data_image_mb_vec[instance_index]);
instance.set_gdb_port(gdb_port_vec[instance_index]);
+ instance.set_fail_fast(fail_fast_vec[instance_index]);
std::optional<std::vector<CuttlefishConfig::DisplayConfig>>
binding_displays_configs;
diff --git a/host/commands/assemble_cvd/flags_defaults.h b/host/commands/assemble_cvd/flags_defaults.h
index 9027613eb..8354e92b5 100644
--- a/host/commands/assemble_cvd/flags_defaults.h
+++ b/host/commands/assemble_cvd/flags_defaults.h
@@ -234,3 +234,6 @@
// Whether to use sandbox2 to lock down host processes where policies exist
#define CF_DEFAULTS_HOST_SANDBOX false
+
+// Whether to exit when heuristics predict the boot will not complete
+#define CF_DEFAULTS_FAIL_FAST true
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.cc b/host/commands/kernel_log_monitor/kernel_log_server.cc
index 2ced97aaf..0c08dc84a 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.cc
+++ b/host/commands/kernel_log_monitor/kernel_log_server.cc
@@ -43,6 +43,7 @@ constexpr struct {
enum EventFormat {
kBare, // Just an event, no extra data
kKeyValuePair, // <stage> <key>=<value>
+ kPrefix, // <stage> <more data>
};
constexpr struct {
@@ -51,11 +52,14 @@ constexpr struct {
EventFormat format; // how the log message is formatted
} kStageTable[] = {
{cuttlefish::kBootStartedMessage, Event::BootStarted, kBare},
+ {cuttlefish::kBootPendingMessage, Event::BootPending, kPrefix},
{cuttlefish::kBootCompletedMessage, Event::BootCompleted, kBare},
- {cuttlefish::kBootFailedMessage, Event::BootFailed, kKeyValuePair},
- {cuttlefish::kMobileNetworkConnectedMessage, Event::MobileNetworkConnected, kBare},
+ {cuttlefish::kBootFailedMessage, Event::BootFailed, kPrefix},
+ {cuttlefish::kMobileNetworkConnectedMessage, Event::MobileNetworkConnected,
+ kBare},
{cuttlefish::kWifiConnectedMessage, Event::WifiNetworkConnected, kBare},
- {cuttlefish::kEthernetConnectedMessage, Event::EthernetNetworkConnected, kBare},
+ {cuttlefish::kEthernetConnectedMessage, Event::EthernetNetworkConnected,
+ kBare},
{cuttlefish::kAdbdStartedMessage, Event::AdbdStarted, kBare},
{cuttlefish::kFastbootdStartedMessage, Event::FastbootStarted, kBare},
{cuttlefish::kFastbootStartedMessage, Event::FastbootStarted, kBare},
@@ -137,7 +141,11 @@ bool KernelLogServer::HandleIncomingMessage() {
auto pos = line_.find(stage);
if (std::string::npos != pos) {
// Log the stage
- LOG(INFO) << stage;
+ if (format == kPrefix) {
+ LOG(INFO) << line_.substr(pos);
+ } else {
+ LOG(INFO) << stage;
+ }
Json::Value message;
message["event"] = event;
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.h b/host/commands/kernel_log_monitor/kernel_log_server.h
index ec33f7185..2e5f8e24d 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.h
+++ b/host/commands/kernel_log_monitor/kernel_log_server.h
@@ -42,7 +42,8 @@ enum Event : int32_t {
* that we're booting a device.
*/
DisplayPowerModeChanged = 10,
- FastbootStarted = 11
+ FastbootStarted = 11,
+ BootPending = 12,
};
enum class SubscriptionAction {
diff --git a/host/commands/run_cvd/boot_state_machine.cc b/host/commands/run_cvd/boot_state_machine.cc
index 02a9ce842..986939dcb 100644
--- a/host/commands/run_cvd/boot_state_machine.cc
+++ b/host/commands/run_cvd/boot_state_machine.cc
@@ -31,6 +31,7 @@
#include "host/commands/kernel_log_monitor/utils.h"
#include "host/commands/run_cvd/validate.h"
#include "host/libs/command_util/runner/defs.h"
+#include "host/libs/command_util/util.h"
#include "host/libs/config/feature.h"
DEFINE_int32(reboot_notification_fd, CF_DEFAULTS_REBOOT_NOTIFICATION_FD,
@@ -92,6 +93,9 @@ SharedFD DaemonizeLauncher(const CuttlefishConfig& config) {
LOG(INFO) << "Virtual device booted successfully";
} else if (exit_code == RunnerExitCodes::kVirtualDeviceBootFailed) {
LOG(ERROR) << "Virtual device failed to boot";
+ if (!instance.fail_fast()) {
+ LOG(ERROR) << "Device has been left running for debug";
+ }
} else {
LOG(ERROR) << "Unexpected exit code: " << exit_code;
}
@@ -170,10 +174,13 @@ Result<SharedFD> ProcessLeader(
// launcher process
class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
public:
- INJECT(CvdBootStateMachine(AutoSetup<ProcessLeader>::Type& process_leader,
- KernelLogPipeProvider& kernel_log_pipe_provider))
+ INJECT(
+ CvdBootStateMachine(AutoSetup<ProcessLeader>::Type& process_leader,
+ KernelLogPipeProvider& kernel_log_pipe_provider,
+ const CuttlefishConfig::InstanceSpecific& instance))
: process_leader_(process_leader),
kernel_log_pipe_provider_(kernel_log_pipe_provider),
+ instance_(instance),
state_(kBootStarted) {}
~CvdBootStateMachine() {
@@ -249,6 +256,17 @@ class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
}
auto sent_code = OnBootEvtReceived(boot_events_pipe);
if (sent_code) {
+ if (!BootCompleted()) {
+ if (!instance_.fail_fast()) {
+ LOG(ERROR) << "Device running, likely in a bad state";
+ break;
+ }
+ auto monitor_res = GetLauncherMonitorFromInstance(instance_, 5);
+ CHECK(monitor_res.ok()) << monitor_res.error().FormatForEnv();
+ auto fail_res = RunLauncherAction(*monitor_res, LauncherAction::kFail,
+ std::optional<int>());
+ CHECK(fail_res.ok()) << fail_res.error().FormatForEnv();
+ }
break;
}
}
@@ -301,6 +319,7 @@ class CvdBootStateMachine : public SetupFeature, public KernelLogPipeConsumer {
AutoSetup<ProcessLeader>::Type& process_leader_;
KernelLogPipeProvider& kernel_log_pipe_provider_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
std::thread boot_event_handler_;
SharedFD fg_launcher_pipe_;
diff --git a/host/commands/run_cvd/server_loop_impl.cpp b/host/commands/run_cvd/server_loop_impl.cpp
index 29fdfd61d..8506245cf 100644
--- a/host/commands/run_cvd/server_loop_impl.cpp
+++ b/host/commands/run_cvd/server_loop_impl.cpp
@@ -206,6 +206,20 @@ void ServerLoopImpl::HandleActionWithNoData(const LauncherAction action,
}
break;
}
+ case LauncherAction::kFail: {
+ auto stop = process_monitor.StopMonitoredProcesses();
+ if (stop.ok()) {
+ auto response = LauncherResponse::kSuccess;
+ client->Write(&response, sizeof(response));
+ std::exit(RunnerExitCodes::kVirtualDeviceBootFailed);
+ } else {
+ auto response = LauncherResponse::kError;
+ client->Write(&response, sizeof(response));
+ LOG(ERROR) << "Failed to stop subprocesses:\n"
+ << stop.error().FormatForEnv();
+ }
+ break;
+ }
case LauncherAction::kStatus: {
// TODO(schuffelen): Return more information on a side channel
auto response = LauncherResponse::kSuccess;
diff --git a/host/commands/start/main.cc b/host/commands/start/main.cc
index 15d6646c4..1f5ca8d07 100644
--- a/host/commands/start/main.cc
+++ b/host/commands/start/main.cc
@@ -266,6 +266,7 @@ std::unordered_set<std::string> kBoolFlags = {
"vhost_user_vsock",
"chromeos_boot",
"enable_host_sandbox",
+ "fail_fast",
};
struct BooleanFlag {
diff --git a/host/libs/command_util/runner/defs.h b/host/libs/command_util/runner/defs.h
index 0b237268a..43ee9dc52 100644
--- a/host/libs/command_util/runner/defs.h
+++ b/host/libs/command_util/runner/defs.h
@@ -55,6 +55,7 @@ enum RunnerExitCodes : int {
// Actions supported by the launcher server
enum class LauncherAction : char {
kExtended = 'A', ///< expect additional information to follow
+ kFail = 'F',
kPowerwash = 'P',
kRestart = 'R',
kStatus = 'I',
diff --git a/host/libs/command_util/util.cc b/host/libs/command_util/util.cc
index 3bbb79cc9..561bdd8d9 100644
--- a/host/libs/command_util/util.cc
+++ b/host/libs/command_util/util.cc
@@ -33,6 +33,7 @@ namespace {
bool IsShortAction(const LauncherAction action) {
switch (action) {
+ case LauncherAction::kFail:
case LauncherAction::kPowerwash:
case LauncherAction::kRestart:
case LauncherAction::kStatus:
diff --git a/host/libs/config/config_constants.h b/host/libs/config/config_constants.h
index eaa71cab8..c0b8bfb34 100644
--- a/host/libs/config/config_constants.h
+++ b/host/libs/config/config_constants.h
@@ -27,6 +27,7 @@ inline constexpr char kCuttlefishInstanceEnvVarName[] = "CUTTLEFISH_INSTANCE";
inline constexpr char kVsocUserPrefix[] = "vsoc-";
inline constexpr char kCvdNamePrefix[] = "cvd-";
inline constexpr char kBootStartedMessage[] = "VIRTUAL_DEVICE_BOOT_STARTED";
+inline constexpr char kBootPendingMessage[] = "VIRTUAL_DEVICE_BOOT_PENDING";
inline constexpr char kBootCompletedMessage[] = "VIRTUAL_DEVICE_BOOT_COMPLETED";
inline constexpr char kBootFailedMessage[] = "VIRTUAL_DEVICE_BOOT_FAILED";
inline constexpr char kMobileNetworkConnectedMessage[] =
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 04d8c3e47..87843ae43 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -571,6 +571,7 @@ class CuttlefishConfig {
bool protected_vm() const;
bool mte() const;
std::string boot_slot() const;
+ bool fail_fast() const;
// Kernel and bootloader logging
bool enable_kernel_log() const;
@@ -775,6 +776,7 @@ class CuttlefishConfig {
void set_mte(bool mte);
void set_boot_slot(const std::string& boot_slot);
void set_grpc_socket_path(const std::string& sockets);
+ void set_fail_fast(bool fail_fast);
// Kernel and bootloader logging
void set_enable_kernel_log(bool enable_kernel_log);
diff --git a/host/libs/config/cuttlefish_config_instance.cpp b/host/libs/config/cuttlefish_config_instance.cpp
index 70532c91f..60db2534b 100644
--- a/host/libs/config/cuttlefish_config_instance.cpp
+++ b/host/libs/config/cuttlefish_config_instance.cpp
@@ -876,6 +876,14 @@ std::string CuttlefishConfig::InstanceSpecific::boot_slot() const {
return (*Dictionary())[kBootSlot].asString();
}
+static constexpr char kFailFast[] = "fail_fast";
+void CuttlefishConfig::MutableInstanceSpecific::set_fail_fast(bool fail_fast) {
+ (*Dictionary())[kFailFast] = fail_fast;
+}
+bool CuttlefishConfig::InstanceSpecific::fail_fast() const {
+ return (*Dictionary())[kFailFast].asBool();
+}
+
static constexpr char kEnableWebRTC[] = "enable_webrtc";
void CuttlefishConfig::MutableInstanceSpecific::set_enable_webrtc(bool enable_webrtc) {
(*Dictionary())[kEnableWebRTC] = enable_webrtc;