910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/client/common/remoting_client.h"

#include <cstdint>
#include <memory>
#include <string>
#include <string_view>
#include <utility>

#include "base/functional/callback.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/notimplemented.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "components/webrtc/thread_wrapper.h"
#include "remoting/base/directory_service_client.h"
#include "remoting/base/oauth_token_info.h"
#include "remoting/base/passthrough_oauth_token_getter.h"
#include "remoting/client/common/client_status_observer.h"
#include "remoting/client/common/frame_consumer_wrapper.h"
#include "remoting/client/common/logging.h"
#include "remoting/proto/control.pb.h"
#include "remoting/proto/remoting/v1/host_info.pb.h"
#include "remoting/proto/remoting/v1/remote_support_host_messages.pb.h"
#include "remoting/protocol/chromium_port_allocator_factory.h"
#include "remoting/protocol/chromium_socket_factory.h"
#include "remoting/protocol/client_authentication_config.h"
#include "remoting/protocol/connection_to_host.h"
#include "remoting/protocol/errors.h"
#include "remoting/protocol/host_stub.h"
#include "remoting/protocol/ice_config_fetcher_default.h"
#include "remoting/protocol/jingle_session.h"
#include "remoting/protocol/jingle_session_manager.h"
#include "remoting/protocol/negotiating_client_authenticator.h"
#include "remoting/protocol/network_settings.h"
#include "remoting/protocol/session_config.h"
#include "remoting/protocol/transport.h"
#include "remoting/protocol/transport_context.h"
#include "remoting/protocol/webrtc_connection_to_host.h"
#include "remoting/signaling/ftl_client_uuid_device_id_provider.h"
#include "remoting/signaling/ftl_signal_strategy.h"
#include "remoting/signaling/signaling_address.h"
#include "remoting/signaling/signaling_id_util.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/webrtc/api/video_codecs/sdp_video_format.h"

namespace remoting {

namespace {
constexpr std::int32_t kMinBitrateBps = 10485760;
}

RemotingClient::RemotingClient(
    base::OnceClosure quit_closure,
    protocol::FrameConsumer* frame_consumer,
    base::WeakPtr<protocol::AudioStub> audio_stream_consumer,
    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
    : quit_closure_(std::move(quit_closure)),
      frame_consumer_(frame_consumer),
      audio_stream_consumer_(std::move(audio_stream_consumer)),
      url_loader_factory_(url_loader_factory) {
  CHECK(frame_consumer_);
}

RemotingClient::~RemotingClient() {
  if (signal_strategy_) {
    signal_strategy_->RemoveListener(this);
  }
  observers_.Notify(&ClientStatusObserver::OnClientDestroyed);
}

void RemotingClient::StartSession(std::string_view support_access_code,
                                  OAuthTokenInfo oauth_token_info) {
  CHECK(!session_manager_);
  CHECK_EQ(support_access_code.length(), 12UL);
  CHECK_GT(oauth_token_info.access_token().length(), 0UL);
  CHECK_GT(oauth_token_info.user_email().length(), 0UL);

  host_id_ = support_access_code.substr(0, 7);
  // Though the host-side impl generates a 5 digit 'secret', the authenticator
  // considers the full 12-digit access code to be the secret.
  host_secret_ = support_access_code;

  // TODO: joedow - If we need to support sessions > 1 hour, we will need to
  // provide a method for refreshing the access token.
  oauth_token_info_ = std::move(oauth_token_info);
  oauth_token_getter_ =
      std::make_unique<PassthroughOAuthTokenGetter>(oauth_token_info_);
  directory_service_client_ = std::make_unique<DirectoryServiceClient>(
      oauth_token_getter_.get(), url_loader_factory_);

  // base::Unretained is sound because this instance owns the service client
  // and callbacks will not be run after destruction.
  CLIENT_LOG << "Retrieving host information for id: " << host_id_;
  directory_service_client_->GetManagedChromeOsHost(
      host_id_,
      base::BindOnce(&RemotingClient::OnGetManagedChromeOsHostRetrieved,
                     base::Unretained(this)));
}

void RemotingClient::OnGetManagedChromeOsHostRetrieved(
    const HttpStatus& status,
    std::unique_ptr<apis::v1::GetManagedChromeOsHostResponse> response) {
  if (!status.ok()) {
    LOG(ERROR) << "Failed to retrieve host information. code: "
               << static_cast<int>(status.error_code())
               << ", message: " << status.error_message();
    RunQuitClosure();
    return;
  }

  if (!response->has_chrome_os_host()) {
    LOG(ERROR) << "Directory response did not include a chrome_os_host value";
    RunQuitClosure();
    return;
  }

  chrome_os_host_.reset(response->release_chrome_os_host());
  if (!chrome_os_host_->has_ftl_id()) {
    LOG(ERROR) << "Directory response did not include an ftl_id value";
    RunQuitClosure();
    return;
  }

  CLIENT_LOG << "Initializing signaling...";
  signal_strategy_ = std::make_unique<FtlSignalStrategy>(
      std::make_unique<PassthroughOAuthTokenGetter>(oauth_token_info_),
      url_loader_factory_, std::make_unique<FtlClientUuidDeviceIdProvider>());
  signal_strategy_->AddListener(this);
  signal_strategy_->Connect();
}

void RemotingClient::StartConnection() {
  CLIENT_LOG << "Creating transport context...";
  webrtc::ThreadWrapper::EnsureForCurrentMessageLoop();
  scoped_refptr<protocol::TransportContext> transport_context =
      new protocol::TransportContext(
          std::make_unique<protocol::ChromiumPortAllocatorFactory>(),
          webrtc::ThreadWrapper::current()->SocketServer(),
          std::make_unique<protocol::IceConfigFetcherDefault>(
              url_loader_factory_, oauth_token_getter_.get()),
          protocol::TransportRole::CLIENT);
  // WebrtcVideoRendererAdapter only supports I420 so we request AV1-Profile0.
  transport_context->set_preferred_video_format(
      webrtc::SdpVideoFormat::AV1Profile0());

  CLIENT_LOG << "Creating session manager...";
  auto protocol_config = protocol::CandidateSessionConfig::CreateDefault();
  protocol_config->set_webrtc_supported(true);
  if (!audio_stream_consumer_) {
    protocol_config->DisableAudioChannel();
  }
  session_manager_ =
      std::make_unique<protocol::JingleSessionManager>(signal_strategy_.get());
  session_manager_->set_protocol_config(std::move(protocol_config));

  CLIENT_LOG << "Creating session...";
  auto host_signaling_id = NormalizeSignalingId(chrome_os_host_->ftl_id());
  protocol::ClientAuthenticationConfig client_auth_config = {};
  client_auth_config.host_id = host_id_;
  client_auth_config.fetch_secret_callback = base::BindRepeating(
      [](const std::string& secret, bool pairing_supported,
         const protocol::SecretFetchedCallback& secret_fetched_callback) {
        secret_fetched_callback.Run(secret);
      },
      host_secret_);
  auto session = session_manager_->Connect(
      SignalingAddress(host_signaling_id),
      std::make_unique<protocol::NegotiatingClientAuthenticator>(
          signal_strategy_->GetLocalAddress().id(), host_signaling_id,
          std::move(client_auth_config)));

  CLIENT_LOG << "Creating video renderer...";
  video_renderer_ = std::make_unique<FrameConsumerWrapper>(frame_consumer_);

  CLIENT_LOG << "Establishing connection to host...";
  connection_ = std::make_unique<protocol::WebrtcConnectionToHost>();
  connection_->set_client_stub(this);
  connection_->set_clipboard_stub(this);
  connection_->set_video_renderer(video_renderer_.get());
  if (audio_stream_consumer_) {
    connection_->InitializeAudio(
        /*audio_decode_task_runner=*/base::ThreadPool::
            CreateSingleThreadTaskRunner(
                {base::TaskPriority::HIGHEST},
                base::SingleThreadTaskRunnerThreadMode::DEDICATED),
        audio_stream_consumer_);
  }
  connection_->Connect(std::move(session), transport_context, this);
  protocol::NetworkSettings network_settings{
      protocol::NetworkSettings::NAT_TRAVERSAL_FULL};
  connection_->ApplyNetworkSettings(network_settings);
}

void RemotingClient::StopSession() {
  CLIENT_LOG << "Shutting down connection to host...";
  if (connection_) {
    connection_->Disconnect(ErrorCode::OK);
    connection_.reset();
  }
  if (signal_strategy_) {
    // Delay tearing down the signaling channel to increase the likelihood that
    // the host processes the connection state change.
    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE,
        base::BindOnce(&SignalStrategy::Disconnect,
                       base::Unretained(signal_strategy_.get())),
        base::Seconds(2));
  }
  return;
}

void RemotingClient::AddObserver(ClientStatusObserver* observer) {
  observers_.AddObserver(observer);
}

void RemotingClient::RemoveObserver(ClientStatusObserver* observer) {
  observers_.RemoveObserver(observer);
}

base::WeakPtr<RemotingClient> RemotingClient::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

void RemotingClient::SetCapabilities(
    const protocol::Capabilities& capabilities) {
  NOTIMPLEMENTED();
}

void RemotingClient::SetPairingResponse(
    const protocol::PairingResponse& pairing_response) {
  NOTIMPLEMENTED();
}

void RemotingClient::DeliverHostMessage(
    const protocol::ExtensionMessage& message) {
  NOTIMPLEMENTED();
}

void RemotingClient::SetVideoLayout(const protocol::VideoLayout& layout) {
  NOTIMPLEMENTED();
}

void RemotingClient::SetTransportInfo(
    const protocol::TransportInfo& transport_info) {
  NOTIMPLEMENTED();
}

void RemotingClient::SetActiveDisplay(
    const protocol::ActiveDisplay& active_display) {
  NOTIMPLEMENTED();
}

void RemotingClient::InjectClipboardEvent(
    const protocol::ClipboardEvent& event) {
  NOTIMPLEMENTED();
}

void RemotingClient::SetCursorShape(
    const protocol::CursorShapeInfo& cursor_shape) {
  NOTIMPLEMENTED();
}

void RemotingClient::SetHostCursorPosition(
    const protocol::HostCursorPosition& position) {
  NOTIMPLEMENTED();
}

void RemotingClient::SetKeyboardLayout(const protocol::KeyboardLayout& layout) {
  NOTIMPLEMENTED();
}

void RemotingClient::OnConnectionState(protocol::ConnectionToHost::State state,
                                       protocol::ErrorCode error) {
  CLIENT_LOG << "OnConnectionState change: "
             << protocol::ConnectionToHost::StateToString(state);
  if (state == protocol::ConnectionToHost::State::CONNECTED &&
      connection_->host_stub()) {
    protocol::PeerConnectionParameters peer_connection_params;
    peer_connection_params.set_preferred_min_bitrate_bps(kMinBitrateBps);
    connection_->host_stub()->ControlPeerConnection(peer_connection_params);
    observers_.Notify(&ClientStatusObserver::OnConnected);
  } else if (state == protocol::ConnectionToHost::State::CLOSED) {
    StopSession();
    observers_.Notify(&ClientStatusObserver::OnDisconnected);
  } else if (state == protocol::ConnectionToHost::State::FAILED) {
    StopSession();
    observers_.Notify(&ClientStatusObserver::OnConnectionFailed);
  }
}

void RemotingClient::OnConnectionReady(bool ready) {
  CLIENT_LOG << "RemotingClient::OnConnectionReady: " << ready;
}

void RemotingClient::OnRouteChanged(const std::string& channel_name,
                                    const protocol::TransportRoute& route) {
  CLIENT_LOG << "Using " << protocol::TransportRoute::GetTypeString(route.type)
             << " connection for " << channel_name << " channel";
}

void RemotingClient::OnSignalStrategyStateChange(SignalStrategy::State state) {
  switch (state) {
    case SignalStrategy::CONNECTING:
      CLIENT_LOG << "Signaling channel is being established.";
      break;
    case SignalStrategy::CONNECTED:
      CLIENT_LOG << "Signaling channel has been established for: "
                 << signal_strategy_->GetLocalAddress().id();
      StartConnection();
      break;
    case SignalStrategy::DISCONNECTED:
      auto error = signal_strategy_->GetError();
      auto error_code = ErrorCode::OK;
      if (error != SignalStrategy::Error::OK) {
        LOG(ERROR) << "Signaling channel has been closed due to error: "
                   << error;
        // TODO: joedow - Map error to error_code.
      } else {
        CLIENT_LOG << "Signaling channel has been closed.";
      }
      if (connection_) {
        connection_->Disconnect(error_code);
      }
      RunQuitClosure();
      break;
  }
}

bool RemotingClient::OnSignalStrategyIncomingStanza(
    const jingle_xmpp::XmlElement* stanza) {
  return false;
}

void RemotingClient::RunQuitClosure() {
  if (quit_closure_) {
    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, std::move(quit_closure_));
  }
}

}  // namespace remoting