// 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 "extensions/browser/api/audio/audio_service_lacros.h"
#include "base/ranges/algorithm.h"
#include "chromeos/lacros/lacros_service.h"
#include "extensions/browser/api/audio/audio_service_utils.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace {
const char* const kServiceNotAvailableErrorMsg =
"Audio devices crosapi interface is not available";
bool IsAudioCrosapiAvailable() {
chromeos::LacrosService* service = chromeos::LacrosService::Get();
DCHECK(service);
return service->IsAvailable<crosapi::mojom::AudioService>();
}
crosapi::mojom::AudioService& GetAudioCrosapi() {
chromeos::LacrosService* service = chromeos::LacrosService::Get();
DCHECK(service);
return *(service->GetRemote<crosapi::mojom::AudioService>());
}
} // namespace
namespace extensions {
AudioServiceLacros::CrosapiObserver::CrosapiObserver() = default;
AudioServiceLacros::CrosapiObserver::~CrosapiObserver() = default;
void AudioServiceLacros::CrosapiObserver::OnDeviceListChanged(
std::vector<crosapi::mojom::AudioDeviceInfoPtr> devices) {
DeviceInfoList result;
base::ranges::transform(devices, std::back_inserter(result),
extensions::ConvertAudioDeviceInfoFromMojom);
for (auto& observer : observer_list_) {
observer.OnDevicesChanged(result);
}
}
void AudioServiceLacros::CrosapiObserver::OnLevelChanged(const std::string& id,
int32_t level) {
for (auto& observer : observer_list_) {
observer.OnLevelChanged(id, level);
}
}
void AudioServiceLacros::CrosapiObserver::OnMuteChanged(bool is_input,
bool is_muted) {
for (auto& observer : observer_list_) {
observer.OnMuteChanged(is_input, is_muted);
}
}
void AudioServiceLacros::CrosapiObserver::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void AudioServiceLacros::CrosapiObserver::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void AudioServiceLacros::AddObserver(Observer* observer) {
crosapi_observer_.AddObserver(observer);
}
void AudioServiceLacros::RemoveObserver(Observer* observer) {
crosapi_observer_.RemoveObserver(observer);
}
AudioServiceLacros::AudioServiceLacros() {
if (IsAudioCrosapiAvailable()) {
GetAudioCrosapi().AddAudioChangeObserver(
receiver_.BindNewPipeAndPassRemote());
} else {
LOG(ERROR) << "Failed to add audio crosapi observer. "
<< kServiceNotAvailableErrorMsg;
}
}
AudioServiceLacros::~AudioServiceLacros() = default;
void AudioServiceLacros::GetDevices(
const api::audio::DeviceFilter* filter,
base::OnceCallback<void(bool, DeviceInfoList)> extapi_callback) {
if (!IsAudioCrosapiAvailable()) {
LOG(ERROR) << "GetDevices. " << kServiceNotAvailableErrorMsg;
DeviceInfoList devices_empty;
std::move(extapi_callback).Run(false, std::move(devices_empty));
return;
}
auto crosapi_callback = base::BindOnce(
[](base::OnceCallback<void(bool, DeviceInfoList)> extapi_callback,
std::optional<std::vector<crosapi::mojom::AudioDeviceInfoPtr>>
crosapi_devices) {
bool result_out = false;
DeviceInfoList devices_out;
if (crosapi_devices) {
result_out = true;
base::ranges::transform(*crosapi_devices,
std::back_inserter(devices_out),
extensions::ConvertAudioDeviceInfoFromMojom);
}
std::move(extapi_callback).Run(result_out, std::move(devices_out));
},
std::move(extapi_callback));
auto crosapi_filter = ConvertDeviceFilterToMojom(filter);
GetAudioCrosapi().GetDevices(std::move(crosapi_filter),
std::move(crosapi_callback));
}
void AudioServiceLacros::SetActiveDeviceLists(
const DeviceIdList* input_devices,
const DeviceIdList* output_devives,
base::OnceCallback<void(bool)> callback) {
if (!IsAudioCrosapiAvailable()) {
LOG(ERROR) << "SetActiveDevices. " << kServiceNotAvailableErrorMsg;
std::move(callback).Run(false);
return;
}
auto ids = ConvertDeviceIdListsToMojom(input_devices, output_devives);
GetAudioCrosapi().SetActiveDeviceLists(std::move(ids), std::move(callback));
}
void AudioServiceLacros::SetDeviceSoundLevel(
const std::string& device_id,
int level_value,
base::OnceCallback<void(bool)> callback) {
if (!IsAudioCrosapiAvailable()) {
LOG(ERROR) << "SetDeviceSoundLevel. " << kServiceNotAvailableErrorMsg;
std::move(callback).Run(false);
return;
}
auto properties = crosapi::mojom::AudioDeviceProperties::New(level_value);
GetAudioCrosapi().SetProperties(device_id, std::move(properties),
std::move(callback));
}
void AudioServiceLacros::SetMute(bool is_input,
bool value,
base::OnceCallback<void(bool)> callback) {
if (!IsAudioCrosapiAvailable()) {
LOG(ERROR) << "SetMute. " << kServiceNotAvailableErrorMsg;
std::move(callback).Run(false);
return;
}
crosapi::mojom::StreamType stream_type =
is_input ? crosapi::mojom::StreamType::kInput
: crosapi::mojom::StreamType::kOutput;
GetAudioCrosapi().SetMute(stream_type, value, std::move(callback));
}
void AudioServiceLacros::GetMute(
bool is_input,
base::OnceCallback<void(bool, bool)> callback) {
if (!IsAudioCrosapiAvailable()) {
LOG(ERROR) << "GetMute. " << kServiceNotAvailableErrorMsg;
std::move(callback).Run(false, false);
return;
}
crosapi::mojom::StreamType stream_type =
is_input ? crosapi::mojom::StreamType::kInput
: crosapi::mojom::StreamType::kOutput;
GetAudioCrosapi().GetMute(stream_type, std::move(callback));
}
AudioService::Ptr AudioService::CreateInstance(
AudioDeviceIdCalculator* id_calculator) {
return std::make_unique<AudioServiceLacros>();
}
} // namespace extensions