blob: ae6c9c50345bc8fefbb6cedca0c5918616f80b31 [file] [log] [blame]
/*
* Copyright (C) 2019 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 "tools/base/deploy/installer/base_install.h"
#include <sys/wait.h>
#include "base_install.h"
#include "tools/base/deploy/common/event.h"
#include "tools/base/deploy/common/utils.h"
#include "tools/base/deploy/installer/command_cmd.h"
#include "tools/base/deploy/installer/patch_applier.h"
namespace deploy {
deploy::BaseInstallCommand::BaseInstallCommand(Workspace& workspace)
: Command(workspace) {}
void BaseInstallCommand::ParseParameters(
const proto::InstallerRequest& request) {
if (!request.has_install_info_request()) {
ErrEvent("Unable to get install info.");
return;
}
install_info_ = request.install_info_request();
ready_to_run_ = true;
}
bool BaseInstallCommand::SendApkToPackageManager(
const proto::PatchInstruction& patch, const std::string& session_id) {
Phase p("DeltaInstallCommand::SendApkToPackageManager");
// Open a stream to the package manager to write to.
std::string output;
std::string error;
std::vector<std::string> parameters;
parameters.emplace_back("package");
parameters.emplace_back("install-write");
parameters.emplace_back("-S");
parameters.emplace_back(to_string(patch.dst_filesize()));
parameters.emplace_back(session_id);
std::string apk = patch.src_absolute_path();
parameters.emplace_back(apk.substr(apk.rfind('/') + 1));
for (std::string& parameter : parameters) {
LogEvent(parameter);
}
int pm_stdin, pid;
Executor::Get().ForkAndExec("cmd", parameters, &pm_stdin, nullptr, nullptr,
&pid);
PatchApplier patchApplier;
bool patch_result = patchApplier.ApplyPatchToFD(patch, pm_stdin);
// Clean up
close(pm_stdin);
int status;
waitpid(pid, &status, 0);
// Patch failed ?
if (!patch_result) {
ErrEvent("Patching '"_s + patch.src_absolute_path() + "' failed");
return false;
}
// PM failed ?
bool success = WIFEXITED(status) && (WEXITSTATUS(status) == 0);
if (!success) {
ErrEvent("Error while sending APKs to PM");
ErrEvent(output);
return false;
}
return true;
}
bool BaseInstallCommand::CreateInstallSession(
std::string* output, std::vector<std::string>* options) {
// Use inheritance so we can skip unchanged APKs in cases where
// the application uses splits.
if (install_info_.inherit()) {
options->emplace_back("-p");
options->emplace_back(install_info_.package_name());
}
CmdCommand cmd(workspace_);
return cmd.CreateInstallSession(output, *options);
}
bool BaseInstallCommand::SendApksToPackageManager(
const std::string& session_id) {
CmdCommand cmd(workspace_);
// For all apks involved, stream the patched content to the Package Manager
for (const proto::PatchInstruction& patch :
install_info_.patchinstructions()) {
// Skip if we are inheriting and this apk did not change
if (install_info_.inherit() && patch.patches().size() == 0) {
LogEvent("Skipping '"_s + patch.src_absolute_path() +
"' since inheriting mode and apk did not change");
continue;
}
bool send_result = SendApkToPackageManager(patch, session_id);
if (!send_result) {
std::string abort_output;
cmd.AbortInstall(session_id, &abort_output);
ErrEvent("Unable to stream '"_s + patch.src_absolute_path() + "' to PM");
return false;
}
LogEvent("Streaming succeeded for '"_s + patch.src_absolute_path() + "'");
}
return true;
}
} // namespace deploy