blob: 8390fc70486cf4e20278d97ef13d15b16cd6d6dc [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/assistant/proxy/service_controller_proxy.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/optional.h"
#include "chromeos/assistant/internal/cros_display_connection.h"
#include "chromeos/assistant/internal/internal_util.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/services/assistant/proxy/libassistant_service_host.h"
#include "chromeos/services/assistant/public/cpp/features.h"
#include "chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h"
#include "chromeos/services/assistant/public/cpp/migration/libassistant_v1_api.h"
#include "chromeos/services/libassistant/libassistant_service.h"
#include "libassistant/shared/internal_api/assistant_manager_internal.h"
#include "libassistant/shared/internal_api/fuchsia_api_helper.h"
namespace chromeos {
namespace assistant {
// TODO(b/171748795): Most of the work that is done here right now (especially
// the work related to starting Libassistant) should be moved to the mojom
// service.
namespace {
using libassistant::mojom::ServiceState;
constexpr base::Feature kChromeOSAssistantDogfood{
"ChromeOSAssistantDogfood", base::FEATURE_DISABLED_BY_DEFAULT};
constexpr char kServersideDogfoodExperimentId[] = "20347368";
constexpr char kServersideOpenAppExperimentId[] = "39651593";
constexpr char kServersideResponseProcessingV2ExperimentId[] = "1793869";
struct StartArguments {
StartArguments() = default;
StartArguments(StartArguments&&) = default;
StartArguments& operator=(StartArguments&&) = default;
~StartArguments() = default;
assistant_client::ActionModule* action_module;
assistant_client::FuchsiaApiDelegate* fuchsia_api_delegate;
assistant_client::AssistantManagerDelegate* assistant_manager_delegate;
assistant_client::ConversationStateListener* conversation_state_listener;
assistant_client::DeviceStateListener* device_state_listener;
CrosDisplayConnection* display_connection;
std::string libassistant_config;
std::string locale;
std::string locale_override;
bool spoken_feedback_enabled;
ServiceControllerProxy::AuthTokens auth_tokens;
};
void FillServerExperimentIds(std::vector<std::string>* server_experiment_ids) {
if (base::FeatureList::IsEnabled(kChromeOSAssistantDogfood)) {
server_experiment_ids->emplace_back(kServersideDogfoodExperimentId);
}
if (base::FeatureList::IsEnabled(features::kAssistantAppSupport))
server_experiment_ids->emplace_back(kServersideOpenAppExperimentId);
server_experiment_ids->emplace_back(
kServersideResponseProcessingV2ExperimentId);
}
void SetServerExperiments(
assistant_client::AssistantManagerInternal* assistant_manager_internal) {
std::vector<std::string> server_experiment_ids;
FillServerExperimentIds(&server_experiment_ids);
if (server_experiment_ids.size() > 0) {
assistant_manager_internal->AddExtraExperimentIds(server_experiment_ids);
}
}
void SetInternalOptions(
assistant_client::AssistantManagerInternal* assistant_manager_internal,
const std::string& locale,
bool spoken_feedback_enabled) {
auto* internal_options =
assistant_manager_internal->CreateDefaultInternalOptions();
SetAssistantOptions(internal_options, locale, spoken_feedback_enabled);
internal_options->SetClientControlEnabled(
assistant::features::IsRoutinesEnabled());
if (!features::IsVoiceMatchDisabled())
internal_options->EnableRequireVoiceMatchVerification();
assistant_manager_internal->SetOptions(*internal_options, [](bool success) {
DVLOG(2) << "set options: " << success;
});
}
// TODO(b/171748795): This should all be migrated to the mojom service, which
// should be responsible for the complete creation of the Libassistant
// objects.
// Note: this method will be called from the mojom (background) thread.
void InitializeAssistantManager(
StartArguments arguments,
assistant_client::AssistantManager* assistant_manager,
assistant_client::AssistantManagerInternal* assistant_manager_internal) {
SetInternalOptions(assistant_manager_internal, arguments.locale,
arguments.spoken_feedback_enabled);
assistant_manager_internal->SetDisplayConnection(
arguments.display_connection);
assistant_manager_internal->SetLocaleOverride(arguments.locale_override);
assistant_manager_internal->RegisterActionModule(arguments.action_module);
assistant_manager_internal->SetAssistantManagerDelegate(
arguments.assistant_manager_delegate);
assistant_manager_internal->GetFuchsiaApiHelperOrDie()->SetFuchsiaApiDelegate(
arguments.fuchsia_api_delegate);
assistant_manager->AddConversationStateListener(
arguments.conversation_state_listener);
assistant_manager->AddDeviceStateListener(arguments.device_state_listener);
SetServerExperiments(assistant_manager_internal);
assistant_manager->SetAuthTokens(arguments.auth_tokens);
}
} // namespace
ServiceControllerProxy::ServiceControllerProxy(
LibassistantServiceHost* host,
mojo::PendingRemote<chromeos::libassistant::mojom::ServiceController>
client)
: host_(host),
service_controller_remote_(std::move(client)),
state_observer_receiver_(this) {
service_controller_remote_->AddAndFireStateObserver(
state_observer_receiver_.BindNewPipeAndPassRemote());
}
ServiceControllerProxy::~ServiceControllerProxy() = default;
void ServiceControllerProxy::Start(
assistant_client::ActionModule* action_module,
assistant_client::FuchsiaApiDelegate* fuchsia_api_delegate,
assistant_client::AssistantManagerDelegate* assistant_manager_delegate,
assistant_client::ConversationStateListener* conversation_state_listener,
assistant_client::DeviceStateListener* device_state_listener,
AssistantEventObserver* event_observer,
const std::string& libassistant_config,
const std::string& locale,
const std::string& locale_override,
bool spoken_feedback_enabled,
const AuthTokens& auth_tokens,
base::OnceClosure done_callback) {
// Start can only be called once (unless Stop() was called).
DCHECK_EQ(state_, State::kStopped);
state_ = State::kStarting;
pending_display_connection_ = std::make_unique<CrosDisplayConnection>(
event_observer, /*feedback_ui_enabled=*/true,
assistant::features::IsMediaSessionIntegrationEnabled());
// The mojom service will create the |AssistantManager|.
service_controller_remote_->Start(libassistant_config);
// We need to initialize the |AssistantManager| once it's created and before
// it's started, so we register a callback to do just that.
StartArguments arguments;
arguments.action_module = action_module;
arguments.fuchsia_api_delegate = fuchsia_api_delegate;
arguments.assistant_manager_delegate = assistant_manager_delegate;
arguments.conversation_state_listener = conversation_state_listener;
arguments.device_state_listener = device_state_listener;
arguments.display_connection = pending_display_connection_.get();
arguments.locale = locale;
arguments.locale_override = locale_override;
arguments.spoken_feedback_enabled = spoken_feedback_enabled;
arguments.auth_tokens = auth_tokens;
host_->SetInitializeCallback(
base::BindOnce(InitializeAssistantManager, std::move(arguments)));
on_start_done_callback_ = std::move(done_callback);
}
void ServiceControllerProxy::Stop() {
// We can not cleanly stop if we're still starting.
DCHECK_NE(state_, State::kStarting);
state_ = State::kStopped;
service_controller_remote_->Stop();
// display_connection_ is used by the assistant manager and can only be
// deleted once we have confirmation the assistant manager is gone.
// so we do not reset it here but in |OnStateChanged| instead.
}
void ServiceControllerProxy::UpdateInternalOptions(
const std::string& locale,
bool spoken_feedback_enabled) {
SetInternalOptions(assistant_manager_internal(), locale,
spoken_feedback_enabled);
}
void ServiceControllerProxy::SetAuthTokens(const AuthTokens& tokens) {
assistant_manager()->SetAuthTokens(tokens);
}
bool ServiceControllerProxy::IsStarted() const {
return state_ == State::kStarted;
}
void ServiceControllerProxy::OnStateChanged(ServiceState new_state) {
DVLOG(1) << "Libassistant service state changed to " << new_state;
switch (new_state) {
case ServiceState::kStarted:
DCHECK_EQ(state_, State::kStarting);
FinishCreatingAssistant();
break;
case ServiceState::kRunning:
NOTIMPLEMENTED();
break;
case ServiceState::kStopped:
display_connection_ = nullptr;
break;
}
}
assistant_client::AssistantManager*
ServiceControllerProxy::assistant_manager() {
auto* api = assistant::LibassistantV1Api::Get();
return api ? api->assistant_manager() : nullptr;
}
assistant_client::AssistantManagerInternal*
ServiceControllerProxy::assistant_manager_internal() {
auto* api = assistant::LibassistantV1Api::Get();
return api ? api->assistant_manager_internal() : nullptr;
}
void ServiceControllerProxy::FinishCreatingAssistant() {
DCHECK(on_start_done_callback_.has_value());
DCHECK_NE(pending_display_connection_, nullptr);
state_ = State::kStarted;
display_connection_ = std::move(pending_display_connection_);
std::move(on_start_done_callback_.value()).Run();
}
} // namespace assistant
} // namespace chromeos