blob: 0cd831c2684ad438e801538192eb93cf1c48397b [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(int argc, char** argv) {
deploy::MessagePipeWrapper wrapper(STDIN_FILENO);
std::string data;
if (!wrapper.Read(&data)) {
ErrEvent("Unable to read data on stdin.");
EndPhase();
return;
}
BeginPhase("Parsing input ");
if (!install_info_.ParseFromString(data)) {
ErrEvent("Unable to parse protobuffer request object.");
EndPhase();
return;
}
EndPhase();
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;
workspace_.GetExecutor().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