blob: f7c1d11faa1d739411b79c07d6479587caeeaa80 [file] [log] [blame]
// Copyright 2015 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <functional>
#include <iterator>
#include <sstream>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include "aemu/base/Optional.h"
#include "android/cpu_accelerator.h"
#include "android/emulation/CpuAccelerator.h"
#include "android/cmdline-definitions.h"
#include "android/avd/info.h"
#include "android/emulation/control/AndroidAgentFactory.h"
#include "android/emulation/control/globals_agent.h"
#include "android/emulator-check/PlatformInfo.h"
using CommandReturn = std::pair<int, std::string>;
// using android::emulation::injectConsoleAgents;
AndroidOptions emptyOptions{};
AndroidOptions* sAndroid_cmdLineOptions = &emptyOptions;
AvdInfo* sAndroid_avdInfo = nullptr;
AndroidHwConfig s_hwConfig = {0};
AvdInfoParams sAndroid_avdInfoParams = {0};
std::string sCmdlLine;
LanguageSettings s_languageSettings = {0};
AUserConfig* s_userConfig = nullptr;
bool sKeyCodeForwarding = false;
// /* this indicates that guest has mounted data partition */
int s_guest_data_partition_mounted = 0;
// /* this indicates that guest has boot completed */
bool s_guest_boot_completed = 0;
bool s_arm_snapshot_save_completed = 0;
bool s_host_emulator_is_headless = 0;
// /* are we using the emulator in the android mode or plain qemu? */
bool s_android_qemu_mode = true;
// /* are we using android-emu libraries for a minimal configuration? */
// Min config mode and fuchsia mode are equivalent, at least for now.
bool s_min_config_qemu_mode = false;
// /* is android-emu running Fuchsia? */
int s_android_snapshot_update_timer = 0;
static const QAndroidGlobalVarsAgent globalVarsAgent = {
.avdParams = []() { return &sAndroid_avdInfoParams; },
.avdInfo =
[]() {
// Do not access the info before it is injected!
return sAndroid_avdInfo;
},
.hw = []() { return &s_hwConfig; },
// /* this indicates that guest has mounted data partition */
.guest_data_partition_mounted =
[]() { return s_guest_data_partition_mounted; },
// /* this indicates that guest has boot completed */
.guest_boot_completed = []() { return s_guest_boot_completed; },
.arm_snapshot_save_completed =
[]() { return s_arm_snapshot_save_completed; },
.host_emulator_is_headless =
[]() { return s_host_emulator_is_headless; },
// /* are we using the emulator in the android mode or plain qemu? */
.android_qemu_mode = []() { return s_android_qemu_mode; },
// /* are we using android-emu libraries for a minimal configuration? */
.min_config_qemu_mode = []() { return s_min_config_qemu_mode; },
// /* is android-emu running Fuchsia? */
.is_fuchsia = []() { return s_min_config_qemu_mode; },
.android_snapshot_update_timer =
[]() { return s_android_snapshot_update_timer; },
.language = []() { return &s_languageSettings; },
.use_keycode_forwarding = []() { return sKeyCodeForwarding; },
.userConfig = []() { return s_userConfig; },
.android_cmdLineOptions = []() { return sAndroid_cmdLineOptions; },
.inject_cmdLineOptions =
[](AndroidOptions* opts) { sAndroid_cmdLineOptions = opts; },
.has_cmdLineOptions =
[]() {
return globalVarsAgent.android_cmdLineOptions() != nullptr;
},
.android_cmdLine = []() { return (const char*)sCmdlLine.c_str(); },
.inject_android_cmdLine =
[](const char* cmdline) { sCmdlLine = cmdline; },
.inject_language =
[](char* language, char* country, char* locale) {
s_languageSettings.language = language;
s_languageSettings.country = country;
s_languageSettings.locale = locale;
s_languageSettings.changing_language_country_locale =
language || country || locale;
},
.inject_userConfig = [](AUserConfig* config) { s_userConfig = config; },
.set_keycode_forwarding =
[](bool enabled) { sKeyCodeForwarding = enabled; },
.inject_AvdInfo = [](AvdInfo* avd) { sAndroid_avdInfo = avd; },
// /* this indicates that guest has mounted data partition */
.set_guest_data_partition_mounted =
[](int guest_data_partition_mounted) {
s_guest_data_partition_mounted =
guest_data_partition_mounted;
},
// /* this indicates that guest has boot completed */
.set_guest_boot_completed =
[](bool guest_boot_completed) {
s_guest_boot_completed = guest_boot_completed;
},
.set_arm_snapshot_save_completed =
[](bool arm_snapshot_save_completed) {
s_arm_snapshot_save_completed = arm_snapshot_save_completed;
},
.set_host_emulator_is_headless =
[](bool host_emulator_is_headless) {
s_host_emulator_is_headless = host_emulator_is_headless;
},
// /* are we using the emulator in the android mode or plain qemu? */
.set_android_qemu_mode =
[](bool android_qemu_mode) {
s_android_qemu_mode = android_qemu_mode;
},
// /* are we using android-emu libraries for a minimal configuration? */
.set_min_config_qemu_mode =
[](bool min_config_qemu_mode) {
s_min_config_qemu_mode = min_config_qemu_mode;
},
// /* is android-emu running Fuchsia? */
.set_is_fuchsia =
[](bool is_fuchsia) { s_min_config_qemu_mode = is_fuchsia; },
.set_android_snapshot_update_timer =
[](int android_snapshot_update_timer) {
s_android_snapshot_update_timer =
android_snapshot_update_timer;
}
};
namespace android::emulation {
class MockAndroidConsoleFactory : public AndroidConsoleFactory {
public:
const QAndroidGlobalVarsAgent* const android_get_QAndroidGlobalVarsAgent()
const override {
return &globalVarsAgent;
};
};
} // namespace android::emulation
static const int kGenericError = 100;
static CommandReturn help();
// check ability to launch haxm/kvm accelerated VM and exit
// designed for use by Android Studio
static CommandReturn checkCpuAcceleration() {
char* status = nullptr;
AndroidCpuAcceleration capability =
androidCpuAcceleration_getStatus(&status);
std::string message = status ? status : "";
free(status);
return std::make_pair(capability, std::move(message));
}
static CommandReturn checkHyperV() {
return android::GetHyperVStatus();
}
static CommandReturn getCpuInfo() {
auto pair = android::GetCpuInfo();
// GetCpuInfo() returns a set of lines, we need to turn it into a
// single line.
std::replace(pair.second.begin(), pair.second.end(), '\n', '|');
return pair;
}
static CommandReturn getWindowManager() {
const std::string name = android::getWindowManagerName();
return std::make_pair(name.empty() ? kGenericError : 0, name);
}
static CommandReturn getDesktopEnv() {
const std::string name = android::getDesktopEnvironmentName();
return std::make_pair(name.empty() ? kGenericError : 0, name);
}
constexpr struct Option {
const char* arg;
const char* help;
CommandReturn (*handler)();
bool printRawAndStop;
} options[] = {
{"-h", "\tShow this help message", &help, true},
{"-help", "Show this help message", &help, true},
{"--help", "Show this help message", &help, true},
{"accel", "Check the CPU acceleration support", &checkCpuAcceleration},
{"hyper-v", "Check if hyper-v is installed and running (Windows)",
&checkHyperV},
{"cpu-info", "Return the CPU model information", &getCpuInfo},
{"window-mgr", "Return the current window manager name",
&getWindowManager},
{"desktop-env", "Return the current desktop environment name",
&getDesktopEnv},
};
static std::string usage() {
std::ostringstream str;
str <<
R"(Usage: emulator-check <argument1> [<argument2>...]
Performs the set of checks requested in <argumentX> and returns the result in
the following format:
<argument1>:
<return code for <argument1>>
<a single line of text information returned for <argument1>>
<argument1>
<argument2>:
<return code for <argument2>>
<a single line of text information returned for <argument2>>
<argument2>
...
<argumentX> is any of:
)";
for (const auto& option : options) {
str << " " << option.arg << "\t\t" << option.help << '\n';
}
str << '\n';
return str.str();
}
static CommandReturn help() {
return std::make_pair(0, usage());
}
static int error(const char* format, const char* arg = nullptr) {
if (format) {
fprintf(stderr, format, arg);
fprintf(stderr, "\n\n");
}
fprintf(stderr, "%s\n", usage().c_str());
return kGenericError;
}
static int processArguments(int argc, const char* const* argv) {
android::base::Optional<int> retCode;
for (int i = 1; i < argc; ++i) {
const std::string_view arg = argv[i];
const auto opt = std::find_if(
std::begin(options), std::end(options),
[arg](const Option& opt) { return arg == opt.arg; });
if (opt == std::end(options)) {
printf("%s:\n%d\nUnknown argument\n%s\n", arg.data(), kGenericError,
arg.data());
continue;
}
auto handlerRes = opt->handler();
if (!retCode) {
// Remember the first return value for the compatibility with the
// previous version.
retCode = handlerRes.first;
}
if (opt->printRawAndStop) {
printf("%s\n", handlerRes.second.c_str());
break;
}
printf("%s:\n%d\n%s\n%s\n", arg.data(), handlerRes.first,
handlerRes.second.c_str(), arg.data());
}
return retCode.valueOr(kGenericError);
}
int main(int argc, const char* const* argv) {
if (argc < 2) {
return error("Missing a required argument(s)");
}
android::emulation::injectConsoleAgents(
android::emulation::MockAndroidConsoleFactory());
return processArguments(argc, argv);
}