chromium/chromeos/ash/services/assistant/assistant_host.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/services/assistant/assistant_host.h"

#include <memory>

#include "base/check.h"
#include "base/functional/bind.h"
#include "base/task/single_thread_task_runner.h"
#include "chromeos/ash/services/assistant/assistant_manager_service_impl.h"
#include "chromeos/ash/services/assistant/libassistant_service_host.h"
#include "chromeos/ash/services/assistant/public/cpp/features.h"
#include "chromeos/ash/services/libassistant/public/mojom/service_controller.mojom.h"

namespace ash::assistant {

AssistantHost::AssistantHost(AssistantManagerServiceImpl* service)
    : service_(service) {
  if (!assistant::features::IsLibAssistantSandboxEnabled()) {
    background_thread_.Start();
  }
}

AssistantHost::~AssistantHost() {
  if (libassistant_service_) {
    StopLibassistantService();
  }
}

void AssistantHost::StartLibassistantService(LibassistantServiceHost* host) {
  DCHECK(host);

  libassistant_service_host_ = host;
  LaunchLibassistantService();

  BindControllers();
}

void AssistantHost::LaunchLibassistantService() {
  if (assistant::features::IsLibAssistantSandboxEnabled()) {
    libassistant_service_host_->Launch(
        libassistant_service_.BindNewPipeAndPassReceiver());
  } else {
    // A Mojom service runs on the thread where its receiver was bound.
    // So to make |libassistant_service_| run on the background thread, we must
    // create it on the background thread, as it binds its receiver in its
    // constructor.
    background_task_runner()->PostTask(
        FROM_HERE,
        base::BindOnce(
            &AssistantHost::LaunchLibassistantServiceOnBackgroundThread,
            // This is safe because we own the background thread,
            // so when we're deleted the background thread is stopped.
            base::Unretained(this),
            // |libassistant_service_| runs on the current thread, so must
            // be bound here and not on the background thread.
            libassistant_service_.BindNewPipeAndPassReceiver()));
  }

  libassistant_service_.set_disconnect_handler(base::BindOnce(
      &AssistantHost::OnRemoteDisconnected, base::Unretained(this)));
}

void AssistantHost::LaunchLibassistantServiceOnBackgroundThread(
    mojo::PendingReceiver<libassistant::mojom::LibassistantService> client) {
  DCHECK(background_task_runner()->BelongsToCurrentThread());
  DCHECK(libassistant_service_host_);
  libassistant_service_host_->Launch(std::move(client));
}

void AssistantHost::StopLibassistantService() {
  ResetRemote();

  if (assistant::features::IsLibAssistantSandboxEnabled()) {
    libassistant_service_host_->Stop();
  } else {
    // |libassistant_service_| is launched on the background thread, so we have
    // to stop it there as well.
    background_task_runner()->PostTask(
        FROM_HERE,
        base::BindOnce(
            &AssistantHost::StopLibassistantServiceOnBackgroundThread,
            base::Unretained(this)));
  }
}

void AssistantHost::StopLibassistantServiceOnBackgroundThread() {
  DCHECK(background_task_runner()->BelongsToCurrentThread());
  libassistant_service_host_->Stop();
}

void AssistantHost::OnRemoteDisconnected() {
  ResetRemote();
  service_->OnStateChanged(libassistant::mojom::ServiceState::kDisconnected);
}

void AssistantHost::BindControllers() {
  mojo::PendingRemote<libassistant::mojom::AudioInputController>
      pending_audio_input_controller_remote;
  mojo::PendingRemote<libassistant::mojom::AudioOutputDelegate>
      pending_audio_output_delegate_remote;
  mojo::PendingRemote<libassistant::mojom::DeviceSettingsDelegate>
      pending_device_settings_delegate_remote;
  mojo::PendingRemote<libassistant::mojom::MediaDelegate>
      pending_media_delegate_remote;
  mojo::PendingRemote<libassistant::mojom::NotificationDelegate>
      pending_notification_delegate_remote;
  mojo::PendingRemote<libassistant::mojom::PlatformDelegate>
      pending_platform_delegate_remote;
  mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentController>
      pending_speaker_id_enrollment_controller_remote;
  mojo::PendingRemote<libassistant::mojom::TimerDelegate>
      pending_timer_delegate_remote;

  media_delegate_ =
      pending_media_delegate_remote.InitWithNewPipeAndPassReceiver();
  notification_delegate_ =
      pending_notification_delegate_remote.InitWithNewPipeAndPassReceiver();
  platform_delegate_ =
      pending_platform_delegate_remote.InitWithNewPipeAndPassReceiver();
  timer_delegate_ =
      pending_timer_delegate_remote.InitWithNewPipeAndPassReceiver();

  pending_audio_output_delegate_receiver_ =
      pending_audio_output_delegate_remote.InitWithNewPipeAndPassReceiver();
  pending_device_settings_delegate_receiver_ =
      pending_device_settings_delegate_remote.InitWithNewPipeAndPassReceiver();
  libassistant_service_->Bind(
      pending_audio_input_controller_remote.InitWithNewPipeAndPassReceiver(),
      conversation_controller_.BindNewPipeAndPassReceiver(),
      display_controller_.BindNewPipeAndPassReceiver(),
      media_controller_.BindNewPipeAndPassReceiver(),
      service_controller_.BindNewPipeAndPassReceiver(),
      settings_controller_.BindNewPipeAndPassReceiver(),
      pending_speaker_id_enrollment_controller_remote
          .InitWithNewPipeAndPassReceiver(),
      timer_controller_.BindNewPipeAndPassReceiver(),
      std::move(pending_audio_output_delegate_remote),
      std::move(pending_device_settings_delegate_remote),
      std::move(pending_media_delegate_remote),
      std::move(pending_notification_delegate_remote),
      std::move(pending_platform_delegate_remote),
      std::move(pending_timer_delegate_remote));

  audio_input_controller_ = std::move(pending_audio_input_controller_remote);
  speaker_id_enrollment_controller_ =
      std::move(pending_speaker_id_enrollment_controller_remote);
}

scoped_refptr<base::SingleThreadTaskRunner>
AssistantHost::background_task_runner() {
  return background_thread_.task_runner();
}

mojo::PendingRemote<libassistant::mojom::AudioInputController>
AssistantHost::ExtractAudioInputController() {
  DCHECK(audio_input_controller_.is_valid());
  return std::move(audio_input_controller_);
}

mojo::PendingReceiver<libassistant::mojom::AudioOutputDelegate>
AssistantHost::ExtractAudioOutputDelegate() {
  DCHECK(pending_audio_output_delegate_receiver_.is_valid());
  return std::move(pending_audio_output_delegate_receiver_);
}

mojo::PendingReceiver<libassistant::mojom::DeviceSettingsDelegate>
AssistantHost::ExtractDeviceSettingsDelegate() {
  DCHECK(pending_device_settings_delegate_receiver_.is_valid());
  return std::move(pending_device_settings_delegate_receiver_);
}

mojo::PendingReceiver<libassistant::mojom::MediaDelegate>
AssistantHost::ExtractMediaDelegate() {
  DCHECK(media_delegate_.is_valid());
  return std::move(media_delegate_);
}

mojo::PendingReceiver<libassistant::mojom::NotificationDelegate>
AssistantHost::ExtractNotificationDelegate() {
  DCHECK(notification_delegate_.is_valid());
  return std::move(notification_delegate_);
}

mojo::PendingReceiver<libassistant::mojom::PlatformDelegate>
AssistantHost::ExtractPlatformDelegate() {
  DCHECK(platform_delegate_.is_valid());
  return std::move(platform_delegate_);
}

mojo::PendingRemote<libassistant::mojom::SpeakerIdEnrollmentController>
AssistantHost::ExtractSpeakerIdEnrollmentController() {
  DCHECK(speaker_id_enrollment_controller_.is_valid());
  return std::move(speaker_id_enrollment_controller_);
}

mojo::PendingReceiver<libassistant::mojom::TimerDelegate>
AssistantHost::ExtractTimerDelegate() {
  DCHECK(timer_delegate_.is_valid());
  return std::move(timer_delegate_);
}

libassistant::mojom::ConversationController&
AssistantHost::conversation_controller() {
  DCHECK(conversation_controller_.is_bound());
  return *conversation_controller_;
}

libassistant::mojom::DisplayController& AssistantHost::display_controller() {
  DCHECK(display_controller_.is_bound());
  return *display_controller_.get();
}

libassistant::mojom::ServiceController& AssistantHost::service_controller() {
  DCHECK(service_controller_.is_bound());
  return *service_controller_.get();
}

libassistant::mojom::MediaController& AssistantHost::media_controller() {
  DCHECK(media_controller_.is_bound());
  return *media_controller_.get();
}

libassistant::mojom::SettingsController& AssistantHost::settings_controller() {
  DCHECK(settings_controller_.is_bound());
  return *settings_controller_;
}

libassistant::mojom::TimerController& AssistantHost::timer_controller() {
  DCHECK(timer_controller_.is_bound());
  return *timer_controller_.get();
}

void AssistantHost::AddSpeechRecognitionObserver(
    mojo::PendingRemote<libassistant::mojom::SpeechRecognitionObserver>
        observer) {
  libassistant_service_->AddSpeechRecognitionObserver(std::move(observer));
}

void AssistantHost::AddAuthenticationStateObserver(
    mojo::PendingRemote<libassistant::mojom::AuthenticationStateObserver>
        observer) {
  libassistant_service_->AddAuthenticationStateObserver(std::move(observer));
}

void AssistantHost::ResetRemote() {
  libassistant_service_.reset();
  conversation_controller_.reset();
  display_controller_.reset();
  media_controller_.reset();
  service_controller_.reset();
  settings_controller_.reset();
  timer_controller_.reset();
}

}  // namespace ash::assistant