blob: 91967b50605a54be6d24ac4666cdbeff966469f6 [file] [log] [blame]
// Copyright 2017 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 "services/audio/public/cpp/audio_system_to_service_adapter.h"
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
#include "media/audio/audio_device_description.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "services/audio/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace audio {
namespace {
using OnAudioParamsCallback = media::AudioSystem::OnAudioParamsCallback;
using OnDeviceIdCallback = media::AudioSystem::OnDeviceIdCallback;
using OnInputDeviceInfoCallback = media::AudioSystem::OnInputDeviceInfoCallback;
using OnBoolCallback = media::AudioSystem::OnBoolCallback;
using OnDeviceDescriptionsCallback =
media::AudioSystem::OnDeviceDescriptionsCallback;
using media::AudioParameters;
int64_t ToTraceId(base::TimeTicks time) {
return (time - base::TimeTicks()).InNanoseconds();
}
std::string ParamsToString(base::Optional<AudioParameters> params) {
return params ? params->AsHumanReadableString() : "nullopt";
}
enum Action {
kGetInputStreamParameters,
kGetOutputStreamParameters,
kHasInputDevices,
kHasOutputDevices,
kGetInputDeviceDescriptions,
kGetOutputDeviceDescriptions,
kGetAssociatedOutputDeviceID,
kGetInputDeviceInfo
};
enum StreamType { kInput, kOutput };
const char* GetTraceEvent(Action action) {
switch (action) {
case kGetInputStreamParameters:
return "AudioSystemToServiceAdapter::GetInputStreamParameters";
case kGetOutputStreamParameters:
return "AudioSystemToServiceAdapter::GetOutputStreamParameters";
case kHasInputDevices:
return "AudioSystemToServiceAdapter::HasInputDevices";
case kHasOutputDevices:
return "AudioSystemToServiceAdapter::HasOutputDevices";
case kGetInputDeviceDescriptions:
return "AudioSystemToServiceAdapter::GetInputDeviceDescriptions";
case kGetOutputDeviceDescriptions:
return "AudioSystemToServiceAdapter::GetOutputDeviceDescriptions";
case kGetAssociatedOutputDeviceID:
return "AudioSystemToServiceAdapter::GetAssociatedOutputDeviceID";
case kGetInputDeviceInfo:
return "AudioSystemToServiceAdapter::GetInputDeviceInfo";
}
NOTREACHED();
}
void LogUMA(Action action, base::TimeTicks start_time) {
const base::TimeDelta duration = base::TimeTicks::Now() - start_time;
switch (action) {
case kGetInputStreamParameters:
UMA_HISTOGRAM_TIMES(
"Media.AudioService.SystemInfoClient.GetInputStreamParameters",
duration);
return;
case kGetOutputStreamParameters:
UMA_HISTOGRAM_TIMES(
"Media.AudioService.SystemInfoClient.GetOutputStreamParameters",
duration);
return;
case kHasInputDevices:
UMA_HISTOGRAM_TIMES("Media.AudioService.SystemInfoClient.HasInputDevices",
duration);
return;
case kHasOutputDevices:
UMA_HISTOGRAM_TIMES(
"Media.AudioService.SystemInfoClient.HasOutputDevices", duration);
return;
case kGetInputDeviceDescriptions:
UMA_HISTOGRAM_TIMES(
"Media.AudioService.SystemInfoClient.GetInputDeviceDescriptions",
duration);
return;
case kGetOutputDeviceDescriptions:
UMA_HISTOGRAM_TIMES(
"Media.AudioService.SystemInfoClient.GetOutputDeviceDescriptions",
duration);
return;
case kGetAssociatedOutputDeviceID:
UMA_HISTOGRAM_TIMES(
"Media.AudioService.SystemInfoClient.GetAssociatedOutputDeviceID",
duration);
return;
case kGetInputDeviceInfo:
UMA_HISTOGRAM_TIMES(
"Media.AudioService.SystemInfoClient.GetInputDeviceInfo", duration);
return;
}
NOTREACHED();
}
OnAudioParamsCallback WrapGetStreamParametersReply(
StreamType stream_type,
const std::string& device_id,
OnAudioParamsCallback on_params_callback) {
const Action action = (stream_type == kInput) ? kGetInputStreamParameters
: kGetOutputStreamParameters;
const base::TimeTicks start_time = base::TimeTicks::Now();
TRACE_EVENT_ASYNC_BEGIN1("audio", GetTraceEvent(action),
ToTraceId(start_time), "device id", device_id);
return base::BindOnce(
[](Action action, base::TimeTicks start_time,
OnAudioParamsCallback on_params_callback,
const base::Optional<media::AudioParameters>& params) {
TRACE_EVENT_ASYNC_END1("audio", GetTraceEvent(action),
ToTraceId(start_time), "params",
ParamsToString(params));
LogUMA(action, start_time);
std::move(on_params_callback).Run(params);
},
action, start_time, std::move(on_params_callback));
}
OnBoolCallback WrapHasDevicesReply(StreamType stream_type,
OnBoolCallback on_has_devices_callback) {
const Action action =
(stream_type == kInput) ? kHasInputDevices : kHasOutputDevices;
const base::TimeTicks start_time = base::TimeTicks::Now();
TRACE_EVENT_ASYNC_BEGIN0("audio", GetTraceEvent(action),
ToTraceId(start_time));
return base::BindOnce(
[](Action action, base::TimeTicks start_time,
OnBoolCallback on_has_devices_callback, bool answer) {
TRACE_EVENT_ASYNC_END1("audio", GetTraceEvent(action),
ToTraceId(start_time), "answer", answer);
LogUMA(action, start_time);
std::move(on_has_devices_callback).Run(answer);
},
action, start_time, std::move(on_has_devices_callback));
}
OnDeviceDescriptionsCallback WrapGetDeviceDescriptionsReply(
StreamType stream_type,
OnDeviceDescriptionsCallback on_descriptions_callback) {
const Action action = (stream_type == kInput) ? kGetInputDeviceDescriptions
: kGetOutputDeviceDescriptions;
const base::TimeTicks start_time = base::TimeTicks::Now();
TRACE_EVENT_ASYNC_BEGIN0("audio", GetTraceEvent(action),
ToTraceId(start_time));
return base::BindOnce(
[](Action action, base::TimeTicks start_time,
OnDeviceDescriptionsCallback on_descriptions_callback,
media::AudioDeviceDescriptions descriptions) {
TRACE_EVENT_ASYNC_END1("audio", GetTraceEvent(action),
ToTraceId(start_time), "device count",
descriptions.size());
LogUMA(action, start_time);
std::move(on_descriptions_callback).Run(std::move(descriptions));
},
action, start_time, std::move(on_descriptions_callback));
}
OnDeviceIdCallback WrapGetAssociatedOutputDeviceIDReply(
const std::string& input_device_id,
OnDeviceIdCallback on_device_id_callback) {
const base::TimeTicks start_time = base::TimeTicks::Now();
TRACE_EVENT_ASYNC_BEGIN1("audio", GetTraceEvent(kGetAssociatedOutputDeviceID),
ToTraceId(start_time), "input_device_id",
input_device_id);
return base::BindOnce(
[](base::TimeTicks start_time, OnDeviceIdCallback on_device_id_callback,
const base::Optional<std::string>& answer) {
TRACE_EVENT_ASYNC_END1(
"audio", GetTraceEvent(kGetAssociatedOutputDeviceID),
ToTraceId(start_time), "answer", answer.value_or("nullopt"));
LogUMA(kGetAssociatedOutputDeviceID, start_time);
std::move(on_device_id_callback).Run(answer);
},
start_time, std::move(on_device_id_callback));
}
OnInputDeviceInfoCallback WrapGetInputDeviceInfoReply(
const std::string& input_device_id,
OnInputDeviceInfoCallback on_input_device_info_callback) {
const base::TimeTicks start_time = base::TimeTicks::Now();
TRACE_EVENT_ASYNC_BEGIN1("audio", GetTraceEvent(kGetInputDeviceInfo),
ToTraceId(start_time), "input_device_id",
input_device_id);
return base::BindOnce(
[](base::TimeTicks start_time,
OnInputDeviceInfoCallback on_input_device_info_callback,
const base::Optional<AudioParameters>& params,
const base::Optional<std::string>& associated_output_device_id) {
TRACE_EVENT_ASYNC_END2(
"audio", GetTraceEvent(kGetInputDeviceInfo), ToTraceId(start_time),
"params", ParamsToString(params), "associated_output_device_id",
associated_output_device_id.value_or("nullopt"));
LogUMA(kGetInputDeviceInfo, start_time);
std::move(on_input_device_info_callback)
.Run(params, associated_output_device_id);
},
start_time, std::move(on_input_device_info_callback));
}
} // namespace
AudioSystemToServiceAdapter::AudioSystemToServiceAdapter(
std::unique_ptr<service_manager::Connector> connector,
base::TimeDelta disconnect_timeout)
: connector_(std::move(connector)) {
DCHECK(connector_);
DETACH_FROM_THREAD(thread_checker_);
if (disconnect_timeout > base::TimeDelta()) {
disconnect_timer_.emplace(
FROM_HERE, disconnect_timeout, this,
&AudioSystemToServiceAdapter::DisconnectOnTimeout);
}
}
AudioSystemToServiceAdapter::AudioSystemToServiceAdapter(
std::unique_ptr<service_manager::Connector> connector)
: AudioSystemToServiceAdapter(std::move(connector), base::TimeDelta()) {}
AudioSystemToServiceAdapter::~AudioSystemToServiceAdapter() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!!system_info_) {
TRACE_EVENT_NESTABLE_ASYNC_END1("audio",
"AudioSystemToServiceAdapter bound", this,
"disconnect reason", "destroyed");
}
}
void AudioSystemToServiceAdapter::GetInputStreamParameters(
const std::string& device_id,
OnAudioParamsCallback on_params_callback) {
GetSystemInfo()->GetInputStreamParameters(
device_id, mojo::WrapCallbackWithDefaultInvokeIfNotRun(
WrapGetStreamParametersReply(
kInput, device_id, std::move(on_params_callback)),
base::nullopt));
}
void AudioSystemToServiceAdapter::GetOutputStreamParameters(
const std::string& device_id,
OnAudioParamsCallback on_params_callback) {
GetSystemInfo()->GetOutputStreamParameters(
device_id, mojo::WrapCallbackWithDefaultInvokeIfNotRun(
WrapGetStreamParametersReply(
kOutput, device_id, std::move(on_params_callback)),
base::nullopt));
}
void AudioSystemToServiceAdapter::HasInputDevices(
OnBoolCallback on_has_devices_callback) {
GetSystemInfo()->HasInputDevices(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
WrapHasDevicesReply(kInput, std::move(on_has_devices_callback)), false));
}
void AudioSystemToServiceAdapter::HasOutputDevices(
OnBoolCallback on_has_devices_callback) {
GetSystemInfo()->HasOutputDevices(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
WrapHasDevicesReply(kOutput, std::move(on_has_devices_callback)), false));
}
void AudioSystemToServiceAdapter::GetDeviceDescriptions(
bool for_input,
OnDeviceDescriptionsCallback on_descriptions_callback) {
auto reply_callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
WrapCallbackWithDeviceNameLocalization(
std::move(on_descriptions_callback)),
media::AudioDeviceDescriptions());
if (for_input)
GetSystemInfo()->GetInputDeviceDescriptions(
WrapGetDeviceDescriptionsReply(kInput, std::move(reply_callback)));
else
GetSystemInfo()->GetOutputDeviceDescriptions(
WrapGetDeviceDescriptionsReply(kOutput, std::move(reply_callback)));
}
void AudioSystemToServiceAdapter::GetAssociatedOutputDeviceID(
const std::string& input_device_id,
OnDeviceIdCallback on_device_id_callback) {
GetSystemInfo()->GetAssociatedOutputDeviceID(
input_device_id,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
WrapGetAssociatedOutputDeviceIDReply(
input_device_id, std::move(on_device_id_callback)),
base::nullopt));
}
void AudioSystemToServiceAdapter::GetInputDeviceInfo(
const std::string& input_device_id,
OnInputDeviceInfoCallback on_input_device_info_callback) {
GetSystemInfo()->GetInputDeviceInfo(
input_device_id,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
WrapGetInputDeviceInfoReply(input_device_id,
std::move(on_input_device_info_callback)),
base::nullopt, base::nullopt));
}
mojom::SystemInfo* AudioSystemToServiceAdapter::GetSystemInfo() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!system_info_) {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
"audio", "AudioSystemToServiceAdapter bound", this);
connector_->BindInterface(mojom::kServiceName,
mojo::MakeRequest(&system_info_));
system_info_.set_connection_error_handler(
base::BindOnce(&AudioSystemToServiceAdapter::OnConnectionError,
base::Unretained(this)));
DCHECK(system_info_);
}
if (disconnect_timer_)
disconnect_timer_->Reset();
return system_info_.get();
}
void AudioSystemToServiceAdapter::DisconnectOnTimeout() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (system_info_.IsExpectingResponse()) {
if (disconnect_timer_)
disconnect_timer_->Reset();
TRACE_EVENT_NESTABLE_ASYNC_INSTANT0("audio", "Timeout: expecting responce",
this);
return;
}
TRACE_EVENT_NESTABLE_ASYNC_END1("audio", "AudioSystemToServiceAdapter bound",
this, "disconnect reason", "timeout");
system_info_.reset();
}
void AudioSystemToServiceAdapter::OnConnectionError() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
TRACE_EVENT_NESTABLE_ASYNC_END1("audio", "AudioSystemToServiceAdapter bound",
this, "disconnect reason",
"connection error");
system_info_.reset();
}
} // namespace audio