#include "remoting/host/cloud_heartbeat_service_client.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "base/functional/callback.h"
#include "base/strings/stringize_macros.h"
#include "remoting/base/environment_details.h"
#include "remoting/base/instance_identity_token_getter.h"
#include "remoting/base/oauth_token_getter_impl.h"
#include "remoting/base/protobuf_http_client.h"
#include "remoting/host/version.h"
#include "remoting/proto/google/internal/remoting/cloud/v1alpha/empty.pb.h"
#include "remoting/proto/google/internal/remoting/cloud/v1alpha/remote_access_host.pb.h"
#include "remoting/proto/google/internal/remoting/cloud/v1alpha/remote_access_service.pb.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace remoting {
namespace {
using Empty = google::internal::remoting::cloud::v1alpha::Empty;
using RemoteAccessHost =
google::internal::remoting::cloud::v1alpha::RemoteAccessHost;
using SendHeartbeatRequest =
google::internal::remoting::cloud::v1alpha::SendHeartbeatRequest;
using UpdateRemoteAccessHostRequest =
google::internal::remoting::cloud::v1alpha::UpdateRemoteAccessHostRequest;
}
CloudHeartbeatServiceClient::CloudHeartbeatServiceClient(
const std::string& directory_id,
OAuthTokenGetter* oauth_token_getter,
InstanceIdentityTokenGetter* instance_identity_token_getter,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: directory_id_(directory_id),
client_(CloudServiceClient::CreateForChromotingRobotAccount(
oauth_token_getter,
url_loader_factory)),
instance_identity_token_getter_(instance_identity_token_getter) {}
CloudHeartbeatServiceClient::~CloudHeartbeatServiceClient() = default;
void CloudHeartbeatServiceClient::SendFullHeartbeat(
bool is_initial_heartbeat,
std::optional<std::string> signaling_id,
std::optional<std::string> offline_reason,
HeartbeatResponseCallback callback) {
instance_identity_token_getter_->RetrieveToken(base::BindOnce(
&CloudHeartbeatServiceClient::SendFullHeartbeatWithIdToken,
weak_factory_.GetWeakPtr(), is_initial_heartbeat, std::move(signaling_id),
std::move(offline_reason), std::move(callback)));
}
void CloudHeartbeatServiceClient::SendFullHeartbeatWithIdToken(
bool is_initial_heartbeat,
std::optional<std::string> signaling_id,
std::optional<std::string> offline_reason,
HeartbeatResponseCallback callback,
std::string_view instance_identity_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (offline_reason.has_value() && !offline_reason->empty()) {
MakeUpdateRemoteAccessHostCall(
signaling_id, offline_reason, instance_identity_token,
base::BindOnce(&CloudHeartbeatServiceClient::OnReportHostOffline,
weak_factory_.GetWeakPtr(), std::move(callback)));
} else if (is_initial_heartbeat) {
MakeUpdateRemoteAccessHostCall(
signaling_id, offline_reason, instance_identity_token,
base::BindOnce(
&CloudHeartbeatServiceClient::OnUpdateRemoteAccessHostResponse,
weak_factory_.GetWeakPtr(), std::move(callback)));
} else {
client_->SendHeartbeat(
directory_id_, instance_identity_token,
base::BindOnce(&CloudHeartbeatServiceClient::OnSendHeartbeatResponse,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
}
void CloudHeartbeatServiceClient::SendLiteHeartbeat(
HeartbeatResponseCallback callback) {
instance_identity_token_getter_->RetrieveToken(
base::BindOnce(&CloudHeartbeatServiceClient::SendLiteHeartbeatWithIdToken,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void CloudHeartbeatServiceClient::SendLiteHeartbeatWithIdToken(
HeartbeatResponseCallback callback,
std::string_view instance_identity_token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
client_->SendHeartbeat(
directory_id_, instance_identity_token,
base::BindOnce(&CloudHeartbeatServiceClient::OnSendHeartbeatResponse,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void CloudHeartbeatServiceClient::CancelPendingRequests() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
client_->CancelPendingRequests();
}
void CloudHeartbeatServiceClient::OnSendHeartbeatResponse(
HeartbeatResponseCallback callback,
const HttpStatus& status,
std::unique_ptr<Empty>) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
RunHeartbeatResponseCallback(std::move(callback), status);
}
void CloudHeartbeatServiceClient::OnUpdateRemoteAccessHostResponse(
HeartbeatResponseCallback callback,
const HttpStatus& status,
std::unique_ptr<RemoteAccessHost>) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status.ok()) {
SendLiteHeartbeat(std::move(callback));
} else {
RunHeartbeatResponseCallback(std::move(callback), status);
}
}
void CloudHeartbeatServiceClient::OnReportHostOffline(
HeartbeatResponseCallback callback,
const HttpStatus& status,
std::unique_ptr<RemoteAccessHost>) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
RunHeartbeatResponseCallback(std::move(callback), status);
}
void CloudHeartbeatServiceClient::MakeUpdateRemoteAccessHostCall(
std::optional<std::string> signaling_id,
std::optional<std::string> offline_reason,
std::string_view instance_identity_token,
CloudServiceClient::UpdateRemoteAccessHostCallback callback) {
constexpr auto* host_version = STRINGIZE(VERSION);
client_->UpdateRemoteAccessHost(directory_id_, host_version, signaling_id,
offline_reason, GetOperatingSystemName(),
GetOperatingSystemVersion(),
instance_identity_token, std::move(callback));
}
void CloudHeartbeatServiceClient::RunHeartbeatResponseCallback(
HeartbeatResponseCallback callback,
const HttpStatus& status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!status.ok()) {
OnError(std::move(callback), status);
return;
}
std::move(callback).Run(status, std::nullopt,
"",
true,
true);
}
}