/* * 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 "host/libs/config/cuttlefish_config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/libs/utils/environment.h" #include "common/libs/utils/files.h" #include "host/libs/vm_manager/crosvm_manager.h" #include "host/libs/vm_manager/gem5_manager.h" #include "host/libs/vm_manager/qemu_manager.h" namespace cuttlefish { namespace { static constexpr int kDefaultInstance = 1; int InstanceFromString(std::string instance_str) { if (android::base::StartsWith(instance_str, kVsocUserPrefix)) { instance_str = instance_str.substr(std::string(kVsocUserPrefix).size()); } else if (android::base::StartsWith(instance_str, kCvdNamePrefix)) { instance_str = instance_str.substr(std::string(kCvdNamePrefix).size()); } int instance = std::stoi(instance_str); if (instance <= 0) { LOG(INFO) << "Failed to interpret \"" << instance_str << "\" as an id, " << "using instance id " << kDefaultInstance; return kDefaultInstance; } return instance; } int InstanceFromEnvironment() { std::string instance_str = StringFromEnv(kCuttlefishInstanceEnvVarName, ""); if (instance_str.empty()) { // Try to get it from the user instead instance_str = StringFromEnv("USER", ""); if (instance_str.empty()) { LOG(DEBUG) << kCuttlefishInstanceEnvVarName << " and USER unset, using instance id " << kDefaultInstance; return kDefaultInstance; } if (!android::base::StartsWith(instance_str, kVsocUserPrefix)) { // No user or we don't recognize this user LOG(DEBUG) << "Non-vsoc user, using instance id " << kDefaultInstance; return kDefaultInstance; } } return InstanceFromString(instance_str); } const char* kInstances = "instances"; } // namespace const char* const kGpuModeAuto = "auto"; const char* const kGpuModeDrmVirgl = "drm_virgl"; const char* const kGpuModeGfxstream = "gfxstream"; const char* const kGpuModeGfxstreamGuestAngle = "gfxstream_guest_angle"; const char* const kGpuModeGuestSwiftshader = "guest_swiftshader"; const char* const kGpuModeNone = "none"; const char* const kHwComposerAuto = "auto"; const char* const kHwComposerDrm = "drm"; const char* const kHwComposerRanchu = "ranchu"; const char* const kHwComposerNone = "none"; std::string DefaultEnvironmentPath(const char* environment_key, const char* default_value, const char* subpath) { return StringFromEnv(environment_key, default_value) + "/" + subpath; } ConfigFragment::~ConfigFragment() = default; static constexpr char kFragments[] = "fragments"; bool CuttlefishConfig::LoadFragment(ConfigFragment& fragment) const { if (!dictionary_->isMember(kFragments)) { LOG(ERROR) << "Fragments member was missing"; return false; } const Json::Value& json_fragments = (*dictionary_)[kFragments]; if (!json_fragments.isMember(fragment.Name())) { LOG(ERROR) << "Could not find a fragment called " << fragment.Name(); return false; } return fragment.Deserialize(json_fragments[fragment.Name()]); } bool CuttlefishConfig::SaveFragment(const ConfigFragment& fragment) { Json::Value& json_fragments = (*dictionary_)[kFragments]; if (json_fragments.isMember(fragment.Name())) { LOG(ERROR) << "Already have a fragment called " << fragment.Name(); return false; } json_fragments[fragment.Name()] = fragment.Serialize(); return true; } static constexpr char kRootDir[] = "root_dir"; std::string CuttlefishConfig::root_dir() const { return (*dictionary_)[kRootDir].asString(); } void CuttlefishConfig::set_root_dir(const std::string& root_dir) { (*dictionary_)[kRootDir] = root_dir; } static constexpr char kVmManager[] = "vm_manager"; std::string CuttlefishConfig::vm_manager() const { return (*dictionary_)[kVmManager].asString(); } void CuttlefishConfig::set_vm_manager(const std::string& name) { (*dictionary_)[kVmManager] = name; } static SecureHal StringToSecureHal(std::string mode) { std::transform(mode.begin(), mode.end(), mode.begin(), ::tolower); if (mode == "keymint") { return SecureHal::Keymint; } else if (mode == "gatekeeper") { return SecureHal::Gatekeeper; } else { return SecureHal::Unknown; } } static constexpr char kSecureHals[] = "secure_hals"; std::set CuttlefishConfig::secure_hals() const { std::set args_set; for (auto& hal : (*dictionary_)[kSecureHals]) { args_set.insert(StringToSecureHal(hal.asString())); } return args_set; } void CuttlefishConfig::set_secure_hals(const std::set& hals) { Json::Value hals_json_obj(Json::arrayValue); for (const auto& hal : hals) { hals_json_obj.append(hal); } (*dictionary_)[kSecureHals] = hals_json_obj; } static constexpr char kCrosvmBinary[] = "crosvm_binary"; std::string CuttlefishConfig::crosvm_binary() const { return (*dictionary_)[kCrosvmBinary].asString(); } void CuttlefishConfig::set_crosvm_binary(const std::string& crosvm_binary) { (*dictionary_)[kCrosvmBinary] = crosvm_binary; } static constexpr char kGem5DebugFlags[] = "gem5_debug_flags"; std::string CuttlefishConfig::gem5_debug_flags() const { return (*dictionary_)[kGem5DebugFlags].asString(); } void CuttlefishConfig::set_gem5_debug_flags(const std::string& gem5_debug_flags) { (*dictionary_)[kGem5DebugFlags] = gem5_debug_flags; } static constexpr char kWebRTCCertsDir[] = "webrtc_certs_dir"; void CuttlefishConfig::set_webrtc_certs_dir(const std::string& certs_dir) { (*dictionary_)[kWebRTCCertsDir] = certs_dir; } std::string CuttlefishConfig::webrtc_certs_dir() const { return (*dictionary_)[kWebRTCCertsDir].asString(); } static constexpr char kSigServerPort[] = "webrtc_sig_server_port"; void CuttlefishConfig::set_sig_server_port(int port) { (*dictionary_)[kSigServerPort] = port; } int CuttlefishConfig::sig_server_port() const { return (*dictionary_)[kSigServerPort].asInt(); } static constexpr char kSigServerAddress[] = "webrtc_sig_server_addr"; void CuttlefishConfig::set_sig_server_address(const std::string& addr) { (*dictionary_)[kSigServerAddress] = addr; } std::string CuttlefishConfig::sig_server_address() const { return (*dictionary_)[kSigServerAddress].asString(); } static constexpr char kSigServerPath[] = "webrtc_sig_server_path"; void CuttlefishConfig::set_sig_server_path(const std::string& path) { // Don't use SetPath here, it's a URL path not a file system path (*dictionary_)[kSigServerPath] = path; } std::string CuttlefishConfig::sig_server_path() const { return (*dictionary_)[kSigServerPath].asString(); } static constexpr char kSigServerSecure[] = "webrtc_sig_server_secure"; void CuttlefishConfig::set_sig_server_secure(bool secure) { (*dictionary_)[kSigServerSecure] = secure; } bool CuttlefishConfig::sig_server_secure() const { return (*dictionary_)[kSigServerSecure].asBool(); } static constexpr char kSigServerStrict[] = "webrtc_sig_server_strict"; void CuttlefishConfig::set_sig_server_strict(bool strict) { (*dictionary_)[kSigServerStrict] = strict; } bool CuttlefishConfig::sig_server_strict() const { return (*dictionary_)[kSigServerStrict].asBool(); } static constexpr char kHostToolsVersion[] = "host_tools_version"; void CuttlefishConfig::set_host_tools_version( const std::map& versions) { Json::Value json(Json::objectValue); for (const auto& [key, value] : versions) { json[key] = value; } (*dictionary_)[kHostToolsVersion] = json; } std::map CuttlefishConfig::host_tools_version() const { if (!dictionary_->isMember(kHostToolsVersion)) { return {}; } std::map versions; const auto& elem = (*dictionary_)[kHostToolsVersion]; for (auto it = elem.begin(); it != elem.end(); it++) { versions[it.key().asString()] = it->asUInt(); } return versions; } static constexpr char kenableHostUwb[] = "enable_host_uwb"; void CuttlefishConfig::set_enable_host_uwb(bool enable_host_uwb) { (*dictionary_)[kenableHostUwb] = enable_host_uwb; } bool CuttlefishConfig::enable_host_uwb() const { return (*dictionary_)[kenableHostUwb].asBool(); } static constexpr char kenableHostUwbConnector[] = "enable_host_uwb_connector"; void CuttlefishConfig::set_enable_host_uwb_connector(bool enable_host_uwb) { (*dictionary_)[kenableHostUwbConnector] = enable_host_uwb; } bool CuttlefishConfig::enable_host_uwb_connector() const { return (*dictionary_)[kenableHostUwbConnector].asBool(); } static constexpr char kPicaUciPort[] = "pica_uci_port"; int CuttlefishConfig::pica_uci_port() const { return (*dictionary_)[kPicaUciPort].asInt(); } void CuttlefishConfig::set_pica_uci_port(int pica_uci_port) { (*dictionary_)[kPicaUciPort] = pica_uci_port; } static constexpr char kenableHostBluetooth[] = "enable_host_bluetooth"; void CuttlefishConfig::set_enable_host_bluetooth(bool enable_host_bluetooth) { (*dictionary_)[kenableHostBluetooth] = enable_host_bluetooth; } bool CuttlefishConfig::enable_host_bluetooth() const { return (*dictionary_)[kenableHostBluetooth].asBool(); } static constexpr char kenableHostBluetoothConnector[] = "enable_host_bluetooth_connector"; void CuttlefishConfig::set_enable_host_bluetooth_connector(bool enable_host_bluetooth) { (*dictionary_)[kenableHostBluetoothConnector] = enable_host_bluetooth; } bool CuttlefishConfig::enable_host_bluetooth_connector() const { return (*dictionary_)[kenableHostBluetoothConnector].asBool(); } static constexpr char kNetsimRadios[] = "netsim_radios"; void CuttlefishConfig::netsim_radio_enable(NetsimRadio flag) { if (dictionary_->isMember(kNetsimRadios)) { // OR the radio to current set of radios (*dictionary_)[kNetsimRadios] = (*dictionary_)[kNetsimRadios].asInt() | flag; } else { (*dictionary_)[kNetsimRadios] = flag; } } bool CuttlefishConfig::netsim_radio_enabled(NetsimRadio flag) const { return (*dictionary_)[kNetsimRadios].asInt() & flag; } static constexpr char kEnableMetrics[] = "enable_metrics"; void CuttlefishConfig::set_enable_metrics(std::string enable_metrics) { (*dictionary_)[kEnableMetrics] = kUnknown; if (!enable_metrics.empty()) { switch (enable_metrics.at(0)) { case 'y': case 'Y': (*dictionary_)[kEnableMetrics] = kYes; break; case 'n': case 'N': (*dictionary_)[kEnableMetrics] = kNo; break; } } } CuttlefishConfig::Answer CuttlefishConfig::enable_metrics() const { return (CuttlefishConfig::Answer)(*dictionary_)[kEnableMetrics].asInt(); } static constexpr char kMetricsBinary[] = "metrics_binary"; void CuttlefishConfig::set_metrics_binary(const std::string& metrics_binary) { (*dictionary_)[kMetricsBinary] = metrics_binary; } std::string CuttlefishConfig::metrics_binary() const { return (*dictionary_)[kMetricsBinary].asString(); } static constexpr char kExtraKernelCmdline[] = "extra_kernel_cmdline"; void CuttlefishConfig::set_extra_kernel_cmdline( const std::string& extra_cmdline) { Json::Value args_json_obj(Json::arrayValue); for (const auto& arg : android::base::Split(extra_cmdline, " ")) { args_json_obj.append(arg); } (*dictionary_)[kExtraKernelCmdline] = args_json_obj; } std::vector CuttlefishConfig::extra_kernel_cmdline() const { std::vector cmdline; for (const Json::Value& arg : (*dictionary_)[kExtraKernelCmdline]) { cmdline.push_back(arg.asString()); } return cmdline; } static constexpr char kExtraBootconfigArgs[] = "extra_bootconfig_args"; void CuttlefishConfig::set_extra_bootconfig_args( const std::string& extra_bootconfig_args) { Json::Value args_json_obj(Json::arrayValue); for (const auto& arg : android::base::Split(extra_bootconfig_args, " ")) { args_json_obj.append(arg); } (*dictionary_)[kExtraBootconfigArgs] = args_json_obj; } std::vector CuttlefishConfig::extra_bootconfig_args() const { std::vector bootconfig; for (const Json::Value& arg : (*dictionary_)[kExtraBootconfigArgs]) { bootconfig.push_back(arg.asString()); } return bootconfig; } static constexpr char kVirtioMac80211Hwsim[] = "virtio_mac80211_hwsim"; void CuttlefishConfig::set_virtio_mac80211_hwsim(bool virtio_mac80211_hwsim) { (*dictionary_)[kVirtioMac80211Hwsim] = virtio_mac80211_hwsim; } bool CuttlefishConfig::virtio_mac80211_hwsim() const { return (*dictionary_)[kVirtioMac80211Hwsim].asBool(); } static constexpr char kVhostUserMac80211Hwsim[] = "vhost_user_mac80211_hwsim"; void CuttlefishConfig::set_vhost_user_mac80211_hwsim(const std::string& path) { (*dictionary_)[kVhostUserMac80211Hwsim] = path; } std::string CuttlefishConfig::vhost_user_mac80211_hwsim() const { return (*dictionary_)[kVhostUserMac80211Hwsim].asString(); } static constexpr char kWmediumdApiServerSocket[] = "wmediumd_api_server_socket"; void CuttlefishConfig::set_wmediumd_api_server_socket(const std::string& path) { (*dictionary_)[kWmediumdApiServerSocket] = path; } std::string CuttlefishConfig::wmediumd_api_server_socket() const { return (*dictionary_)[kWmediumdApiServerSocket].asString(); } static constexpr char kApRootfsImage[] = "ap_rootfs_image"; std::string CuttlefishConfig::ap_rootfs_image() const { return (*dictionary_)[kApRootfsImage].asString(); } void CuttlefishConfig::set_ap_rootfs_image(const std::string& ap_rootfs_image) { (*dictionary_)[kApRootfsImage] = ap_rootfs_image; } static constexpr char kApKernelImage[] = "ap_kernel_image"; std::string CuttlefishConfig::ap_kernel_image() const { return (*dictionary_)[kApKernelImage].asString(); } void CuttlefishConfig::set_ap_kernel_image(const std::string& ap_kernel_image) { (*dictionary_)[kApKernelImage] = ap_kernel_image; } static constexpr char kWmediumdConfig[] = "wmediumd_config"; void CuttlefishConfig::set_wmediumd_config(const std::string& config) { (*dictionary_)[kWmediumdConfig] = config; } std::string CuttlefishConfig::wmediumd_config() const { return (*dictionary_)[kWmediumdConfig].asString(); } static constexpr char kRootcanalArgs[] = "rootcanal_args"; void CuttlefishConfig::set_rootcanal_args(const std::string& rootcanal_args) { Json::Value args_json_obj(Json::arrayValue); for (const auto& arg : android::base::Split(rootcanal_args, " ")) { args_json_obj.append(arg); } (*dictionary_)[kRootcanalArgs] = args_json_obj; } std::vector CuttlefishConfig::rootcanal_args() const { std::vector rootcanal_args; for (const Json::Value& arg : (*dictionary_)[kRootcanalArgs]) { rootcanal_args.push_back(arg.asString()); } return rootcanal_args; } static constexpr char kRootcanalHciPort[] = "rootcanal_hci_port"; int CuttlefishConfig::rootcanal_hci_port() const { return (*dictionary_)[kRootcanalHciPort].asInt(); } void CuttlefishConfig::set_rootcanal_hci_port(int rootcanal_hci_port) { (*dictionary_)[kRootcanalHciPort] = rootcanal_hci_port; } static constexpr char kRootcanalLinkPort[] = "rootcanal_link_port"; int CuttlefishConfig::rootcanal_link_port() const { return (*dictionary_)[kRootcanalLinkPort].asInt(); } void CuttlefishConfig::set_rootcanal_link_port(int rootcanal_link_port) { (*dictionary_)[kRootcanalLinkPort] = rootcanal_link_port; } static constexpr char kRootcanalLinkBlePort[] = "rootcanal_link_ble_port"; int CuttlefishConfig::rootcanal_link_ble_port() const { return (*dictionary_)[kRootcanalLinkBlePort].asInt(); } void CuttlefishConfig::set_rootcanal_link_ble_port( int rootcanal_link_ble_port) { (*dictionary_)[kRootcanalLinkBlePort] = rootcanal_link_ble_port; } static constexpr char kRootcanalTestPort[] = "rootcanal_test_port"; int CuttlefishConfig::rootcanal_test_port() const { return (*dictionary_)[kRootcanalTestPort].asInt(); } void CuttlefishConfig::set_rootcanal_test_port(int rootcanal_test_port) { (*dictionary_)[kRootcanalTestPort] = rootcanal_test_port; } static constexpr char kRootcanalConfigFile[] = "rootcanal_config_file"; std::string CuttlefishConfig::rootcanal_config_file() const { return (*dictionary_)[kRootcanalConfigFile].asString(); } void CuttlefishConfig::set_rootcanal_config_file( const std::string& rootcanal_config_file) { (*dictionary_)[kRootcanalConfigFile] = DefaultHostArtifactsPath(rootcanal_config_file); } static constexpr char kRootcanalDefaultCommandsFile[] = "rootcanal_default_commands_file"; std::string CuttlefishConfig::rootcanal_default_commands_file() const { return (*dictionary_)[kRootcanalDefaultCommandsFile].asString(); } void CuttlefishConfig::set_rootcanal_default_commands_file( const std::string& rootcanal_default_commands_file) { (*dictionary_)[kRootcanalDefaultCommandsFile] = DefaultHostArtifactsPath(rootcanal_default_commands_file); } /*static*/ CuttlefishConfig* CuttlefishConfig::BuildConfigImpl( const std::string& path) { auto ret = new CuttlefishConfig(); if (ret) { auto loaded = ret->LoadFromFile(path.c_str()); if (!loaded) { delete ret; return nullptr; } } return ret; } /*static*/ std::unique_ptr CuttlefishConfig::GetFromFile(const std::string& path) { return std::unique_ptr(BuildConfigImpl(path)); } // Creates the (initially empty) config object and populates it with values from // the config file if the CUTTLEFISH_CONFIG_FILE env variable is present. // Returns nullptr if there was an error loading from file /*static*/ const CuttlefishConfig* CuttlefishConfig::Get() { auto config_file_path = StringFromEnv(kCuttlefishConfigEnvVarName, GetGlobalConfigFileLink()); static std::shared_ptr config( BuildConfigImpl(config_file_path)); return config.get(); } /*static*/ bool CuttlefishConfig::ConfigExists() { auto config_file_path = StringFromEnv(kCuttlefishConfigEnvVarName, GetGlobalConfigFileLink()); auto real_file_path = AbsolutePath(config_file_path.c_str()); return FileExists(real_file_path); } CuttlefishConfig::CuttlefishConfig() : dictionary_(new Json::Value()) {} // Can't use '= default' on the header because the compiler complains of // Json::Value being an incomplete type CuttlefishConfig::~CuttlefishConfig() = default; CuttlefishConfig::CuttlefishConfig(CuttlefishConfig&&) = default; CuttlefishConfig& CuttlefishConfig::operator=(CuttlefishConfig&&) = default; bool CuttlefishConfig::LoadFromFile(const char* file) { auto real_file_path = AbsolutePath(file); if (real_file_path.empty()) { LOG(ERROR) << "Could not get real path for file " << file; return false; } Json::CharReaderBuilder builder; std::ifstream ifs(real_file_path); std::string errorMessage; if (!Json::parseFromStream(builder, ifs, dictionary_.get(), &errorMessage)) { LOG(ERROR) << "Could not read config file " << file << ": " << errorMessage; return false; } return true; } bool CuttlefishConfig::SaveToFile(const std::string& file) const { std::ofstream ofs(file); if (!ofs.is_open()) { LOG(ERROR) << "Unable to write to file " << file; return false; } ofs << *dictionary_; return !ofs.fail(); } std::string CuttlefishConfig::instances_dir() const { return AbsolutePath(root_dir() + "/instances"); } std::string CuttlefishConfig::InstancesPath( const std::string& file_name) const { return AbsolutePath(instances_dir() + "/" + file_name); } std::string CuttlefishConfig::assembly_dir() const { return AbsolutePath(root_dir() + "/assembly"); } std::string CuttlefishConfig::AssemblyPath( const std::string& file_name) const { return AbsolutePath(assembly_dir() + "/" + file_name); } std::string CuttlefishConfig::instances_uds_dir() const { // Try to use /tmp/cf_avd_{uid}/ for UDS directory. // If it fails, use HOME directory(legacy) instead. auto defaultPath = AbsolutePath("/tmp/cf_avd_" + std::to_string(getuid())); if (!DirectoryExists(defaultPath) || CanAccess(defaultPath, R_OK | W_OK | X_OK)) { return defaultPath; } return instances_dir(); } std::string CuttlefishConfig::InstancesUdsPath( const std::string& file_name) const { return AbsolutePath(instances_uds_dir() + "/" + file_name); } CuttlefishConfig::MutableInstanceSpecific CuttlefishConfig::ForInstance(int num) { return MutableInstanceSpecific(this, std::to_string(num)); } CuttlefishConfig::InstanceSpecific CuttlefishConfig::ForInstance(int num) const { return InstanceSpecific(this, std::to_string(num)); } CuttlefishConfig::InstanceSpecific CuttlefishConfig::ForInstanceName( const std::string& name) const { return ForInstance(InstanceFromString(name)); } CuttlefishConfig::InstanceSpecific CuttlefishConfig::ForDefaultInstance() const { return ForInstance(GetInstance()); } std::vector CuttlefishConfig::Instances() const { const auto& json = (*dictionary_)[kInstances]; std::vector instances; for (const auto& name : json.getMemberNames()) { instances.push_back(CuttlefishConfig::InstanceSpecific(this, name)); } return instances; } std::vector CuttlefishConfig::instance_dirs() const { std::vector result; for (const auto& instance : Instances()) { result.push_back(instance.instance_dir()); result.push_back(instance.instance_uds_dir()); } return result; } static constexpr char kInstanceNames[] = "instance_names"; void CuttlefishConfig::set_instance_names( const std::vector& instance_names) { Json::Value args_json_obj(Json::arrayValue); for (const auto& name : instance_names) { args_json_obj.append(name); } (*dictionary_)[kInstanceNames] = args_json_obj; } std::vector CuttlefishConfig::instance_names() const { // NOTE: The structure of this field needs to remain stable, since // cvd_server may call this on config JSON files from various builds. // // This info is duplicated into its own field here so it is simpler // to keep stable, rather than parsing from Instances()::instance_name. // // Any non-stable changes must be accompanied by an uprev to the // cvd_server major version. std::vector names; for (const Json::Value& name : (*dictionary_)[kInstanceNames]) { names.push_back(name.asString()); } return names; } int GetInstance() { static int instance_id = InstanceFromEnvironment(); return instance_id; } int GetDefaultVsockCid() { // we assume that this function is used to configure CuttlefishConfig once static const int default_vsock_cid = 3 + GetInstance() - 1; return default_vsock_cid; } int GetVsockServerPort(const int base, const int vsock_guest_cid /**< per instance guest cid */) { return base + (vsock_guest_cid - 3); } std::string GetGlobalConfigFileLink() { return StringFromEnv("HOME", ".") + "/.cuttlefish_config.json"; } std::string ForCurrentInstance(const char* prefix) { std::ostringstream stream; stream << prefix << std::setfill('0') << std::setw(2) << GetInstance(); return stream.str(); } int ForCurrentInstance(int base) { return base + GetInstance() - 1; } std::string RandomSerialNumber(const std::string& prefix) { const char hex_characters[] = "0123456789ABCDEF"; std::srand(time(0)); char str[10]; for(int i=0; i<10; i++){ str[i] = hex_characters[rand() % strlen(hex_characters)]; } return prefix + str; } std::string DefaultHostArtifactsPath(const std::string& file_name) { return (StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) + "/") + file_name; } std::string HostBinaryPath(const std::string& binary_name) { #ifdef __ANDROID__ return binary_name; #else return DefaultHostArtifactsPath("bin/" + binary_name); #endif } std::string DefaultGuestImagePath(const std::string& file_name) { return (StringFromEnv("ANDROID_PRODUCT_OUT", StringFromEnv("HOME", "."))) + file_name; } bool HostSupportsQemuCli() { static bool supported = std::system( "/usr/lib/cuttlefish-common/bin/capability_query.py qemu_cli") == 0; return supported; } } // namespace cuttlefish