blob: ce4637ff8e4fa7e65dcfcb74acccd13b2e588787 [file] [log] [blame]
// Copyright 2018 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 "android/base/ProcessControl.h"
#include "android/base/files/PathUtils.h"
#include "android/base/memory/LazyInstance.h"
#include "android/base/Optional.h"
#include "android/base/process-control.h"
#include "android/base/system/System.h"
#include "android/utils/path.h"
#include <functional>
#include <fstream>
#include <sstream>
namespace android {
namespace base {
bool ProcessLaunchParameters::operator==(const ProcessLaunchParameters& other) const {
if (workingDirectory != other.workingDirectory) return false;
if (programPath != other.programPath) return false;
if (argv.size() != other.argv.size()) return false;
for (int i = 0; i < argv.size(); ++i) {
if (argv[i] != other.argv[i]) return false;
}
return true;
}
std::string ProcessLaunchParameters::str() const {
std::stringstream ss;
ss << "Working directory: [" << workingDirectory << "]" << std::endl;
ss << "Program path: [" << programPath << "]" << std::endl;
ss << "Arg count: " << argv.size() << std::endl;
for (int i = 0; i < argv.size(); i++) {
ss << "Argv[ " << i << "] : [" << argv[i] << "]" << std::endl;
}
return ss.str();
}
std::vector<std::string> makeArgvStrings(int argc, const char** argv) {
std::vector<std::string> res;
for (int i = 0; i < argc; ++i) {
res.push_back(argv[i]);
}
return res;
}
ProcessLaunchParameters createLaunchParametersForCurrentProcess(int argc, char** argv) {
ProcessLaunchParameters currentLaunchParams = {
System::get()->getCurrentDirectory(),
PathUtils::join(System::get()->getProgramDirectory(), PathUtils::decompose(argv[0]).back()),
makeArgvStrings(argc, (const char**)argv),
};
return currentLaunchParams;
}
void saveLaunchParameters(const ProcessLaunchParameters& launchParams, StringView filename) {
std::ofstream file(c_str(filename));
file << launchParams.workingDirectory << std::endl;
file << launchParams.programPath << std::endl;
file << launchParams.argv.size() << std::endl;
for (const auto& arg : launchParams.argv) {
// argv[0] is still saved, but can be skipped when launching
file << arg << std::endl;
}
}
ProcessLaunchParameters loadLaunchParameters(StringView filename) {
ProcessLaunchParameters res;
std::ifstream file(filename);
std::string line;
std::getline(file, line);
res.workingDirectory = line;
std::getline(file, line);
res.programPath = line;
std::getline(file, line);
std::istringstream iss(line);
int argc = 0;
iss >> argc;
res.argv.resize(argc);
for (int i = 0; i < argc; i++) {
std::getline(file, line);
res.argv[i] = line;
}
return res;
}
void launchProcessFromParameters(const ProcessLaunchParameters& launchParams, bool useArgv0) {
std::vector<std::string> cmdArgs;
cmdArgs.push_back(launchParams.programPath);
for (int i = 1; i < launchParams.argv.size(); ++i) {
cmdArgs.push_back(launchParams.argv[i]);
}
System::get()->runCommand(cmdArgs);
}
class RestartGlobals {
public:
RestartGlobals() = default;
bool disabled = false;
bool doRestartOnExit = false;
Optional<ProcessLaunchParameters> restartParams = kNullopt;
};
static LazyInstance<RestartGlobals> sRestartGlobals = LAZY_INSTANCE_INIT;
void disableRestart() {
sRestartGlobals->disabled = true;
}
bool isRestartDisabled() {
return sRestartGlobals->disabled;
}
static constexpr char kLaunchParamsFileName[] = "emu-launch-params.txt";
static constexpr char kRestartParam[] = "-is-restart";
void initializeEmulatorRestartParameters(int argc, char** argv, const char* outPath) {
auto params = createLaunchParametersForCurrentProcess(argc, argv);
android::base::saveLaunchParameters(createLaunchParametersForCurrentProcess(argc, argv),
PathUtils::join(outPath, kLaunchParamsFileName));
sRestartGlobals->restartParams.emplace(params);
}
void finalizeEmulatorRestartParameters(const char* dir) {
auto path = PathUtils::join(dir, kLaunchParamsFileName);
if (!System::get()->pathExists(path)) return;
auto params = loadLaunchParameters(path);
// Add or replace the "-is-restart" option
// to make the launch wait for process exit next time
bool hasExistingRestartOpt = false;
int restartArgIndex = 0;
for (const auto& arg : params.argv) {
if (arg == kRestartParam) {
hasExistingRestartOpt = true;
break;
}
++restartArgIndex;
}
std::stringstream ss;
ss << System::get()->getCurrentProcessId();
if (hasExistingRestartOpt) {
params.argv[restartArgIndex + 1] = ss.str();
} else {
params.argv.push_back(kRestartParam);
params.argv.push_back(ss.str());
}
sRestartGlobals->restartParams.emplace(params);
}
void setEmulatorRestartOnExit() {
if (sRestartGlobals->disabled) return;
sRestartGlobals->doRestartOnExit = true;
}
void handleEmulatorRestart() {
auto params = sRestartGlobals->restartParams;
if (sRestartGlobals->doRestartOnExit && params) {
launchProcessFromParameters(*params);
}
}
class EmulatorRestarter {
public:
EmulatorRestarter() = default;
void registerQuit(FunctionView<void()> func) {
mQuitFunc = func;
}
void quit() {
if (mQuitFunc) mQuitFunc();
}
void restart() {
if (!mQuitFunc) return;
android::base::setEmulatorRestartOnExit();
quit();
}
private:
std::function<void()> mQuitFunc;
};
static android::base::LazyInstance<EmulatorRestarter> sRestarter = LAZY_INSTANCE_INIT;
void registerEmulatorQuitCallback(FunctionView<void()> func) {
sRestarter->registerQuit(func);
}
void restartEmulator() {
sRestarter->restart();
}
} // namespace base
} // namespace android
extern "C" {
void handle_emulator_restart() {
android::base::handleEmulatorRestart();
}
}