blob: c620f784833bee66f79aba82d55afd20f613eaf8 [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/base_swap.h"
#include <fcntl.h>
#include <sys/wait.h>
#include "tools/base/bazel/native/matryoshka/doll.h"
#include "tools/base/deploy/common/env.h"
#include "tools/base/deploy/common/event.h"
#include "tools/base/deploy/common/message_pipe_wrapper.h"
#include "tools/base/deploy/common/socket.h"
#include "tools/base/deploy/common/utils.h"
#include "tools/base/deploy/installer/binary_extract.h"
#include "tools/base/deploy/installer/command_cmd.h"
#include "tools/base/deploy/installer/executor/runas_executor.h"
#include "tools/base/deploy/installer/server/install_server.h"
namespace {
// These values are based on FIRST_APPLICATION_UID and LAST_APPLICATION_UID in
// android.os.Process, which we assume are stable since they haven't been
// changed since 2012.
const int kFirstAppUid = 10000;
const int kLastAppUid = 19999;
bool isUserDebug() {
return deploy::Env::build_type().find("userdebug") != std::string::npos;
}
} // namespace
namespace deploy {
void BaseSwapCommand::Run(proto::InstallerResponse* response) {
proto::SwapResponse* swap_response = response->mutable_swap_response();
if (!ExtractBinaries(workspace_.GetTmpFolder(),
{kAgent, kAgentAlt, kInstallServer})) {
swap_response->set_status(proto::SwapResponse::SETUP_FAILED);
ErrEvent("Extracting binaries failed");
return;
}
client_ = StartInstallServer(
workspace_.GetExecutor(), workspace_.GetTmpFolder() + kInstallServer,
package_name_, kInstallServer + "-" + workspace_.GetVersion());
if (!client_) {
if (isUserDebug()) {
swap_response->set_status(
proto::SwapResponse::START_SERVER_FAILED_USERDEBUG);
} else {
swap_response->set_status(proto::SwapResponse::START_SERVER_FAILED);
}
swap_response->set_extra(kInstallServer);
return;
}
proto::SwapRequest request = PrepareAndBuildRequest(swap_response);
Swap(request, swap_response);
ProcessResponse(swap_response);
}
void BaseSwapCommand::Swap(const proto::SwapRequest& swap_request,
proto::SwapResponse* swap_response) {
Phase p("Swap");
if (swap_response->status() != proto::SwapResponse::UNKNOWN) {
return;
}
// Remove process ids that we do not need to swap.
FilterProcessIds(&process_ids_);
// Don't bother with the server if we have no work to do.
if (process_ids_.empty() && extra_agents_count_ == 0) {
LogEvent("No PIDs needs to be swapped");
swap_response->set_status(proto::SwapResponse::OK);
return;
}
// Request for the install-server to open a socket and begin listening for
// agents to connect. Agents connect shortly after they are attached (below).
proto::SwapResponse::Status status = ListenForAgents();
if (status != proto::SwapResponse::OK) {
swap_response->set_status(status);
return;
}
if (!AttachAgents()) {
swap_response->set_status(proto::SwapResponse::AGENT_ATTACH_FAILED);
return;
}
// Request for the install-server to accept a connection for each agent
// attached. The install-server will forward the specified swap request to
// every agent, then return an aggregate list of each agent's response.
proto::InstallServerRequest server_request;
server_request.set_type(proto::InstallServerRequest::HANDLE_REQUEST);
auto send_request = server_request.mutable_send_request();
send_request->set_agent_count(process_ids_.size() + extra_agents_count_);
*send_request->mutable_swap_request() = swap_request;
if (!client_->Write(server_request)) {
swap_response->set_status(proto::SwapResponse::WRITE_TO_SERVER_FAILED);
return;
}
proto::InstallServerResponse server_response;
if (!client_->Read(&server_response) ||
server_response.status() !=
proto::InstallServerResponse::REQUEST_COMPLETED) {
swap_response->set_status(proto::SwapResponse::READ_FROM_SERVER_FAILED);
return;
}
const auto& agent_server_response = server_response.send_response();
for (const auto& agent_response : agent_server_response.agent_responses()) {
// Convert proto events to events.
for (int i = 0; i < agent_response.events_size(); i++) {
const proto::Event& event = agent_response.events(i);
AddRawEvent(ConvertProtoEventToEvent(event));
}
if (agent_response.status() != proto::AgentSwapResponse::OK) {
auto failed_agent = swap_response->add_failed_agents();
*failed_agent = agent_response;
}
}
if (agent_server_response.status() == proto::SendAgentMessageResponse::OK) {
if (swap_response->failed_agents_size() == 0) {
swap_response->set_status(proto::SwapResponse::OK);
} else {
swap_response->set_status(proto::SwapResponse::AGENT_ERROR);
}
return;
}
CmdCommand cmd(workspace_);
std::vector<ProcessRecord> records;
if (cmd.GetProcessInfo(package_name_, &records)) {
for (auto& record : records) {
if (record.crashing) {
swap_response->set_status(proto::SwapResponse::PROCESS_CRASHING);
swap_response->set_extra(record.process_name);
return;
}
if (record.not_responding) {
swap_response->set_status(proto::SwapResponse::PROCESS_NOT_RESPONDING);
swap_response->set_extra(record.process_name);
return;
}
}
}
for (int pid : swap_request.process_ids()) {
const std::string pid_string = to_string(pid);
if (IO::access("/proc/" + pid_string, F_OK) != 0) {
swap_response->set_status(proto::SwapResponse::PROCESS_TERMINATED);
swap_response->set_extra(pid_string);
return;
}
}
swap_response->set_status(proto::SwapResponse::MISSING_AGENT_RESPONSES);
}
void BaseSwapCommand::FilterProcessIds(std::vector<int>* process_ids) {
Phase p("FilterProcessIds");
auto it = process_ids->begin();
while (it != process_ids->end()) {
const int pid = *it;
const std::string pid_path = "/proc/" + to_string(pid);
struct stat proc_dir_stat;
if (IO::stat(pid_path, &proc_dir_stat) < 0) {
LogEvent("Ignoring pid '" + to_string(pid) + "'; could not stat().");
it = process_ids->erase(it);
} else if (proc_dir_stat.st_uid < kFirstAppUid ||
proc_dir_stat.st_uid > kLastAppUid) {
LogEvent("Ignoring pid '" + to_string(pid) +
"'; uid=" + to_string(proc_dir_stat.st_uid) +
" is not in the app uid range.");
it = process_ids->erase(it);
} else {
++it;
}
}
}
proto::SwapResponse::Status BaseSwapCommand::ListenForAgents() const {
Phase("ListenForAgents");
proto::InstallServerRequest server_request;
server_request.set_type(proto::InstallServerRequest::HANDLE_REQUEST);
auto socket_request = server_request.mutable_socket_request();
socket_request->set_socket_name(Socket::kDefaultAddress);
if (!client_->Write(server_request)) {
return proto::SwapResponse::WRITE_TO_SERVER_FAILED;
}
proto::InstallServerResponse server_response;
if (!client_->Read(&server_response)) {
return proto::SwapResponse::READ_FROM_SERVER_FAILED;
}
if (server_response.status() !=
proto::InstallServerResponse::REQUEST_COMPLETED ||
server_response.socket_response().status() !=
proto::OpenAgentSocketResponse::OK) {
return proto::SwapResponse::READY_FOR_AGENTS_NOT_RECEIVED;
}
return proto::SwapResponse::OK;
}
bool BaseSwapCommand::AttachAgents() const {
Phase p("AttachAgents");
CmdCommand cmd(workspace_);
for (int pid : process_ids_) {
std::string output;
LogEvent("Attaching agent: '"_s + agent_path_ + "'");
output = "";
if (!cmd.AttachAgent(pid, agent_path_, {Socket::kDefaultAddress},
&output)) {
ErrEvent("Could not attach agent to process: "_s + output);
return false;
}
}
return true;
}
bool BaseSwapCommand::CheckFilesExist(
const std::vector<std::string>& files,
std::unordered_set<std::string>* missing_files) {
proto::InstallServerRequest request;
request.set_type(proto::InstallServerRequest::HANDLE_REQUEST);
for (const std::string& file : files) {
request.mutable_check_request()->add_files(file);
}
proto::InstallServerResponse response;
if (!client_->Write(request) || !client_->Read(&response)) {
return false;
}
missing_files->insert(response.check_response().missing_files().begin(),
response.check_response().missing_files().end());
return true;
}
} // namespace deploy