blob: 745556a9e62ff15f2a151ee9aca0639a7917f09c [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/ipc/update_service_internal_proxy_posix.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/process/launch.h"
#include "base/task/bind_post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/ipc/ipc_names.h"
#include "chrome/updater/ipc/update_service_dialer.h"
#include "chrome/updater/ipc/update_service_internal_proxy.h"
#include "chrome/updater/mojom/updater_service_internal.mojom.h"
#include "chrome/updater/service_proxy_factory.h"
#include "chrome/updater/update_service_internal.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/util/util.h"
#include "components/named_mojo_ipc_server/named_mojo_ipc_server_client_util.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
#include "mojo/public/cpp/system/isolated_connection.h"
namespace updater {
namespace {
// The maximum amount of time to poll the server's socket for a connection. This
// can take a long time if the server is unable to start because it is blocked
// behind acquiring a prefs lock (for example if the active instance is busy).
constexpr base::TimeDelta kConnectionTimeout = base::Minutes(10);
// Connect to the server.
// `retries` is 0 for the first try, 1 for the first retry, etc.
std::optional<mojo::PlatformChannelEndpoint> ConnectMojo(UpdaterScope scope,
int retries) {
if (retries == 1 && !DialUpdateInternalService(scope)) {
return std::nullopt;
}
return named_mojo_ipc_server::ConnectToServer(
GetUpdateServiceInternalServerName(scope));
}
void Connect(
UpdaterScope scope,
int tries,
base::Time deadline,
base::OnceCallback<void(std::optional<mojo::PlatformChannelEndpoint>)>
connected_callback) {
if (base::Time::Now() > deadline) {
LOG(ERROR) << "Failed to connect to UpdateServiceInternal remote. "
"Connection timed out.";
std::move(connected_callback).Run(std::nullopt);
return;
}
std::optional<mojo::PlatformChannelEndpoint> endpoint =
ConnectMojo(scope, tries);
if (!endpoint) {
VLOG(1) << "Failed to connect to UpdateService remote. "
"No updater exists.";
std::move(connected_callback).Run(std::nullopt);
return;
}
if (endpoint->is_valid()) {
std::move(connected_callback).Run(std::move(endpoint));
return;
}
base::ThreadPool::PostDelayedTask(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&Connect, scope, tries + 1, deadline,
std::move(connected_callback)),
base::Milliseconds(30 * tries));
}
} // namespace
UpdateServiceInternalProxyImpl::UpdateServiceInternalProxyImpl(
UpdaterScope scope)
: scope_(scope) {}
void UpdateServiceInternalProxyImpl::Run(
base::OnceCallback<void(std::optional<RpcError>)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
EnsureConnecting();
remote_->Run(base::BindOnce(
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindPostTaskToCurrentDefault(std::move(callback)),
kErrorIpcDisconnect),
std::nullopt));
}
void UpdateServiceInternalProxyImpl::Hello(
base::OnceCallback<void(std::optional<RpcError>)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
EnsureConnecting();
remote_->Hello(base::BindOnce(
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindPostTaskToCurrentDefault(std::move(callback)),
kErrorIpcDisconnect),
std::nullopt));
}
UpdateServiceInternalProxyImpl::~UpdateServiceInternalProxyImpl() = default;
void UpdateServiceInternalProxyImpl::EnsureConnecting() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (remote_) {
return;
}
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&Connect, scope_, 0,
base::Time::Now() + kConnectionTimeout,
base::BindPostTaskToCurrentDefault(base::BindOnce(
&UpdateServiceInternalProxyImpl::OnConnected, this,
remote_.BindNewPipeAndPassReceiver()))));
}
void UpdateServiceInternalProxyImpl::OnDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
connection_.reset();
remote_.reset();
}
void UpdateServiceInternalProxyImpl::OnConnected(
mojo::PendingReceiver<mojom::UpdateServiceInternal> pending_receiver,
std::optional<mojo::PlatformChannelEndpoint> endpoint) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!endpoint) {
VLOG(2) << "No endpoint received.";
remote_.reset();
return;
}
auto connection = std::make_unique<mojo::IsolatedConnection>();
// Connect `remote_` to the RPC server by fusing its message pipe to the one
// created by `IsolatedConnection::Connect`.
if (!mojo::FusePipes(
std::move(pending_receiver),
mojo::PendingRemote<mojom::UpdateServiceInternal>(
connection->Connect(std::move(endpoint.value())), 0))) {
LOG(ERROR) << "Failed to fuse Mojo pipes for RPC.";
remote_.reset();
return;
}
connection_ = std::move(connection);
// A weak pointer is used here to prevent remote_ from forming a reference
// cycle with this object.
remote_.set_disconnect_handler(
base::BindOnce(&UpdateServiceInternalProxyImpl::OnDisconnected,
weak_factory_.GetWeakPtr()));
}
scoped_refptr<UpdateServiceInternal> CreateUpdateServiceInternalProxy(
UpdaterScope scope) {
return base::MakeRefCounted<UpdateServiceInternalProxy>(
base::MakeRefCounted<UpdateServiceInternalProxyImpl>(scope));
}
} // namespace updater