blob: 02e5d7b4d32c6434b628747263517b6ac99c4b6f [file] [log] [blame]
/*
* Copyright (C) 2020 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/overlay_swap.h"
#include "tools/base/deploy/common/event.h"
#include "tools/base/deploy/common/log.h"
#include "tools/base/deploy/common/utils.h"
#include "tools/base/deploy/installer/command_cmd.h"
#include "tools/base/deploy/installer/executor/executor.h"
#include "tools/base/deploy/installer/executor/runas_executor.h"
#include "tools/base/deploy/sites/sites.h"
namespace deploy {
void OverlaySwapCommand::ParseParameters(
const proto::InstallerRequest& request) {
if (!request.has_overlay_swap_request()) {
return;
}
request_ = request.overlay_swap_request();
std::vector<int> pids(request_.process_ids().begin(),
request_.process_ids().end());
SetSwapParameters(request_.package_name(), pids, request_.extra_agents());
ready_to_run_ = true;
}
std::unique_ptr<proto::SwapRequest>
OverlaySwapCommand::PrepareAndBuildRequest() {
Phase p("PreSwap");
std::unique_ptr<proto::SwapRequest> request(new proto::SwapRequest());
std::string version = workspace_.GetVersion() + "-";
// Determine which agent we need to use.
#if defined(__aarch64__) || defined(__x86_64__)
std::string agent =
request_.arch() == proto::Arch::ARCH_64_BIT ? kAgent : kAgentAlt;
#else
std::string agent = kAgent;
#endif
std::string startup_path = Sites::AppStartupAgent(package_name_);
std::string studio_path = Sites::AppStudio(package_name_);
std::string agent_path = startup_path + version + agent;
std::unordered_set<std::string> missing_files;
if (!CheckFilesExist({startup_path, studio_path, agent_path},
&missing_files)) {
return nullptr;
}
RunasExecutor run_as(package_name_);
std::string error;
bool missing_startup =
missing_files.find(startup_path) != missing_files.end();
bool missing_agent = missing_files.find(agent_path) != missing_files.end();
// Clean up other agents from the startup_agent directory. Because agents are
// versioned (agent-<version#>) we cannot simply copy our agent on top of the
// previous file. If the startup_agent directory exists but our agent cannot
// be found in it, we assume another agent is present and delete it.
if (!missing_startup && missing_agent) {
if (!run_as.Run("rm", {"-f", "-r", startup_path}, nullptr, &error)) {
ErrEvent("Could not remove old agents: " + error);
return nullptr;
}
missing_startup = true;
}
if (missing_startup &&
!run_as.Run("mkdir", {startup_path}, nullptr, &error)) {
ErrEvent("Could not create startup agent directory: " + error);
return nullptr;
}
if (missing_files.find(studio_path) != missing_files.end() &&
!run_as.Run("mkdir", {studio_path}, nullptr, &error)) {
ErrEvent("Could not create .studio directory: " + error);
return nullptr;
}
if (missing_agent &&
!run_as.Run("cp", {"-F", workspace_.GetTmpFolder() + agent, agent_path},
nullptr, &error)) {
ErrEvent("Could not copy binaries: " + error);
return nullptr;
}
SetAgentPath(agent_path);
for (auto& clazz : request_.new_classes()) {
request->add_new_classes()->CopyFrom(clazz);
}
for (auto& clazz : request_.modified_classes()) {
request->add_modified_classes()->CopyFrom(clazz);
}
request->set_package_name(package_name_);
request->set_restart_activity(request_.restart_activity());
request->set_structural_redefinition(request_.structural_redefinition());
request->set_variable_reinitialization(request_.variable_reinitialization());
request->set_overlay_swap(true);
return request;
}
void OverlaySwapCommand::BuildOverlayUpdateRequest(
proto::OverlayUpdateRequest* request) {
request->set_overlay_id(request_.overlay_id());
request->set_expected_overlay_id(request_.expected_overlay_id());
const std::string pkg = request_.package_name();
const std::string overlay_path = Sites::AppOverlays(pkg);
request->set_overlay_path(overlay_path);
for (auto clazz : request_.new_classes()) {
auto file = request->add_files_to_write();
file->set_path(clazz.name() + ".dex");
file->set_allocated_content(clazz.release_dex());
}
for (auto clazz : request_.modified_classes()) {
auto file = request->add_files_to_write();
file->set_path(clazz.name() + ".dex");
file->set_allocated_content(clazz.release_dex());
}
for (auto resource : request_.resource_overlays()) {
auto file = request->add_files_to_write();
file->set_path(resource.path());
file->set_allocated_content(resource.release_content());
}
}
void OverlaySwapCommand::ProcessResponse(proto::SwapResponse* response) {
Phase p("PostSwap");
if (response->status() == proto::SwapResponse::OK ||
request_.always_update_overlay()) {
UpdateOverlay(response);
}
// Do this even if the deployment failed; it's retrieving data unrelated to
// the current deployment. We might want to find a better time to do this.
GetAgentLogs(response);
}
void OverlaySwapCommand::UpdateOverlay(proto::SwapResponse* response) {
Phase p("UpdateOverlay");
bool swap_failed = (response->status() != proto::SwapResponse::OK);
proto::OverlayUpdateRequest req;
BuildOverlayUpdateRequest(&req);
auto resp = client_->UpdateOverlay(req);
if (!resp) {
response->set_status(proto::SwapResponse::INSTALL_SERVER_COM_ERR);
return;
}
response->set_status(OverlayStatusToSwapStatus(resp->status()));
response->set_extra(resp->error_message());
bool should_restart = request_.restart_activity() &&
response->status() == proto::SwapResponse::OK;
CmdCommand cmd(workspace_);
std::string error;
if (should_restart &&
!cmd.UpdateAppInfo("all", request_.package_name(), &error)) {
response->set_status(proto::SwapResponse::ACTIVITY_RESTART_FAILED);
}
if (swap_failed &&
(response->status() == proto::SwapResponse::OK ||
response->status() == proto::SwapResponse::ACTIVITY_RESTART_FAILED)) {
// If we updated overlay even on swap fail or restart fail,
// alter the response accordingly.
response->set_status(proto::SwapResponse::SWAP_FAILED_BUT_OVERLAY_UPDATED);
}
}
void OverlaySwapCommand::GetAgentLogs(proto::SwapResponse* response) {
Phase p("GetAgentLogs");
proto::GetAgentExceptionLogRequest req;
req.set_package_name(request_.package_name());
// If this fails, we don't really care - it's a best-effort situation; don't
// break the deployment because of it. Just log and move on.
auto resp = client_->GetAgentExceptionLog(req);
if (!resp) {
return;
}
for (const auto& log : resp->logs()) {
auto added = response->add_agent_logs();
*added = log;
}
}
proto::SwapResponse::Status OverlaySwapCommand::OverlayStatusToSwapStatus(
proto::OverlayUpdateResponse::Status status) {
switch (status) {
case proto::OverlayUpdateResponse::OK:
return proto::SwapResponse::OK;
case proto::OverlayUpdateResponse::ID_MISMATCH:
return proto::SwapResponse::OVERLAY_ID_MISMATCH;
default:
return proto::SwapResponse::OVERLAY_UPDATE_FAILED;
}
}
} // namespace deploy