1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "services/audio/public/cpp/audio_system_to_service_adapter.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/check_op.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/notreached.h"
13 #include "base/trace_event/trace_event.h"
14 #include "media/audio/audio_device_description.h"
15 #include "mojo/public/cpp/bindings/callback_helpers.h"
16 
17 namespace audio {
18 
19 namespace {
20 
21 using OnAudioParamsCallback = media::AudioSystem::OnAudioParamsCallback;
22 using OnDeviceIdCallback = media::AudioSystem::OnDeviceIdCallback;
23 using OnInputDeviceInfoCallback = media::AudioSystem::OnInputDeviceInfoCallback;
24 using OnBoolCallback = media::AudioSystem::OnBoolCallback;
25 using OnDeviceDescriptionsCallback =
26     media::AudioSystem::OnDeviceDescriptionsCallback;
27 using media::AudioParameters;
28 
ToTraceId(base::TimeTicks time)29 int64_t ToTraceId(base::TimeTicks time) {
30   return (time - base::TimeTicks()).InNanoseconds();
31 }
32 
ParamsToString(base::Optional<AudioParameters> params)33 std::string ParamsToString(base::Optional<AudioParameters> params) {
34   return params ? params->AsHumanReadableString() : "nullopt";
35 }
36 
37 enum Action {
38   kGetInputStreamParameters,
39   kGetOutputStreamParameters,
40   kHasInputDevices,
41   kHasOutputDevices,
42   kGetInputDeviceDescriptions,
43   kGetOutputDeviceDescriptions,
44   kGetAssociatedOutputDeviceID,
45   kGetInputDeviceInfo
46 };
47 
48 enum StreamType { kInput, kOutput };
49 
GetTraceEvent(Action action)50 const char* GetTraceEvent(Action action) {
51   switch (action) {
52     case kGetInputStreamParameters:
53       return "AudioSystemToServiceAdapter::GetInputStreamParameters";
54     case kGetOutputStreamParameters:
55       return "AudioSystemToServiceAdapter::GetOutputStreamParameters";
56     case kHasInputDevices:
57       return "AudioSystemToServiceAdapter::HasInputDevices";
58     case kHasOutputDevices:
59       return "AudioSystemToServiceAdapter::HasOutputDevices";
60     case kGetInputDeviceDescriptions:
61       return "AudioSystemToServiceAdapter::GetInputDeviceDescriptions";
62     case kGetOutputDeviceDescriptions:
63       return "AudioSystemToServiceAdapter::GetOutputDeviceDescriptions";
64     case kGetAssociatedOutputDeviceID:
65       return "AudioSystemToServiceAdapter::GetAssociatedOutputDeviceID";
66     case kGetInputDeviceInfo:
67       return "AudioSystemToServiceAdapter::GetInputDeviceInfo";
68   }
69   NOTREACHED();
70 }
71 
WrapGetStreamParametersReply(StreamType stream_type,const std::string & device_id,OnAudioParamsCallback on_params_callback)72 OnAudioParamsCallback WrapGetStreamParametersReply(
73     StreamType stream_type,
74     const std::string& device_id,
75     OnAudioParamsCallback on_params_callback) {
76   const Action action = (stream_type == kInput) ? kGetInputStreamParameters
77                                                 : kGetOutputStreamParameters;
78   const base::TimeTicks start_time = base::TimeTicks::Now();
79   TRACE_EVENT_ASYNC_BEGIN1("audio", GetTraceEvent(action),
80                            ToTraceId(start_time), "device id", device_id);
81 
82   return base::BindOnce(
83       [](Action action, base::TimeTicks start_time,
84          OnAudioParamsCallback on_params_callback,
85          const base::Optional<media::AudioParameters>& params) {
86         TRACE_EVENT_ASYNC_END1("audio", GetTraceEvent(action),
87                                ToTraceId(start_time), "params",
88                                ParamsToString(params));
89         std::move(on_params_callback).Run(params);
90       },
91       action, start_time, std::move(on_params_callback));
92 }
93 
WrapHasDevicesReply(StreamType stream_type,OnBoolCallback on_has_devices_callback)94 OnBoolCallback WrapHasDevicesReply(StreamType stream_type,
95                                    OnBoolCallback on_has_devices_callback) {
96   const Action action =
97       (stream_type == kInput) ? kHasInputDevices : kHasOutputDevices;
98   const base::TimeTicks start_time = base::TimeTicks::Now();
99   TRACE_EVENT_ASYNC_BEGIN0("audio", GetTraceEvent(action),
100                            ToTraceId(start_time));
101 
102   return base::BindOnce(
103       [](Action action, base::TimeTicks start_time,
104          OnBoolCallback on_has_devices_callback, bool answer) {
105         TRACE_EVENT_ASYNC_END1("audio", GetTraceEvent(action),
106                                ToTraceId(start_time), "answer", answer);
107         std::move(on_has_devices_callback).Run(answer);
108       },
109       action, start_time, std::move(on_has_devices_callback));
110 }
111 
WrapGetDeviceDescriptionsReply(StreamType stream_type,OnDeviceDescriptionsCallback on_descriptions_callback)112 OnDeviceDescriptionsCallback WrapGetDeviceDescriptionsReply(
113     StreamType stream_type,
114     OnDeviceDescriptionsCallback on_descriptions_callback) {
115   const Action action = (stream_type == kInput) ? kGetInputDeviceDescriptions
116                                                 : kGetOutputDeviceDescriptions;
117   const base::TimeTicks start_time = base::TimeTicks::Now();
118   TRACE_EVENT_ASYNC_BEGIN0("audio", GetTraceEvent(action),
119                            ToTraceId(start_time));
120 
121   return base::BindOnce(
122       [](Action action, base::TimeTicks start_time,
123          OnDeviceDescriptionsCallback on_descriptions_callback,
124          media::AudioDeviceDescriptions descriptions) {
125         TRACE_EVENT_ASYNC_END1("audio", GetTraceEvent(action),
126                                ToTraceId(start_time), "device count",
127                                descriptions.size());
128         std::move(on_descriptions_callback).Run(std::move(descriptions));
129       },
130       action, start_time, std::move(on_descriptions_callback));
131 }
132 
WrapGetAssociatedOutputDeviceIDReply(const std::string & input_device_id,OnDeviceIdCallback on_device_id_callback)133 OnDeviceIdCallback WrapGetAssociatedOutputDeviceIDReply(
134     const std::string& input_device_id,
135     OnDeviceIdCallback on_device_id_callback) {
136   const base::TimeTicks start_time = base::TimeTicks::Now();
137   TRACE_EVENT_ASYNC_BEGIN1("audio", GetTraceEvent(kGetAssociatedOutputDeviceID),
138                            ToTraceId(start_time), "input_device_id",
139                            input_device_id);
140 
141   return base::BindOnce(
142       [](base::TimeTicks start_time, OnDeviceIdCallback on_device_id_callback,
143          const base::Optional<std::string>& answer) {
144         TRACE_EVENT_ASYNC_END1(
145             "audio", GetTraceEvent(kGetAssociatedOutputDeviceID),
146             ToTraceId(start_time), "answer", answer.value_or("nullopt"));
147         std::move(on_device_id_callback).Run(answer);
148       },
149       start_time, std::move(on_device_id_callback));
150 }
151 
WrapGetInputDeviceInfoReply(const std::string & input_device_id,OnInputDeviceInfoCallback on_input_device_info_callback)152 OnInputDeviceInfoCallback WrapGetInputDeviceInfoReply(
153     const std::string& input_device_id,
154     OnInputDeviceInfoCallback on_input_device_info_callback) {
155   const base::TimeTicks start_time = base::TimeTicks::Now();
156   TRACE_EVENT_ASYNC_BEGIN1("audio", GetTraceEvent(kGetInputDeviceInfo),
157                            ToTraceId(start_time), "input_device_id",
158                            input_device_id);
159 
160   return base::BindOnce(
161       [](base::TimeTicks start_time,
162          OnInputDeviceInfoCallback on_input_device_info_callback,
163          const base::Optional<AudioParameters>& params,
164          const base::Optional<std::string>& associated_output_device_id) {
165         TRACE_EVENT_ASYNC_END2(
166             "audio", GetTraceEvent(kGetInputDeviceInfo), ToTraceId(start_time),
167             "params", ParamsToString(params), "associated_output_device_id",
168             associated_output_device_id.value_or("nullopt"));
169         std::move(on_input_device_info_callback)
170             .Run(params, associated_output_device_id);
171       },
172       start_time, std::move(on_input_device_info_callback));
173 }
174 
175 }  // namespace
176 
AudioSystemToServiceAdapter(SystemInfoBinder system_info_binder,base::TimeDelta disconnect_timeout)177 AudioSystemToServiceAdapter::AudioSystemToServiceAdapter(
178     SystemInfoBinder system_info_binder,
179     base::TimeDelta disconnect_timeout)
180     : system_info_binder_(std::move(system_info_binder)),
181       disconnect_timeout_(disconnect_timeout) {
182   DCHECK(system_info_binder_);
183   DETACH_FROM_THREAD(thread_checker_);
184 }
185 
AudioSystemToServiceAdapter(SystemInfoBinder system_info_binder)186 AudioSystemToServiceAdapter::AudioSystemToServiceAdapter(
187     SystemInfoBinder system_info_binder)
188     : AudioSystemToServiceAdapter(std::move(system_info_binder),
189                                   base::TimeDelta()) {}
190 
~AudioSystemToServiceAdapter()191 AudioSystemToServiceAdapter::~AudioSystemToServiceAdapter() {
192   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
193   if (system_info_.is_bound()) {
194     TRACE_EVENT_NESTABLE_ASYNC_END1("audio",
195                                     "AudioSystemToServiceAdapter bound", this,
196                                     "disconnect reason", "destroyed");
197   }
198 }
199 
GetInputStreamParameters(const std::string & device_id,OnAudioParamsCallback on_params_callback)200 void AudioSystemToServiceAdapter::GetInputStreamParameters(
201     const std::string& device_id,
202     OnAudioParamsCallback on_params_callback) {
203   GetSystemInfo()->GetInputStreamParameters(
204       device_id, mojo::WrapCallbackWithDefaultInvokeIfNotRun(
205                      WrapGetStreamParametersReply(
206                          kInput, device_id, std::move(on_params_callback)),
207                      base::nullopt));
208 }
209 
GetOutputStreamParameters(const std::string & device_id,OnAudioParamsCallback on_params_callback)210 void AudioSystemToServiceAdapter::GetOutputStreamParameters(
211     const std::string& device_id,
212     OnAudioParamsCallback on_params_callback) {
213   GetSystemInfo()->GetOutputStreamParameters(
214       device_id, mojo::WrapCallbackWithDefaultInvokeIfNotRun(
215                      WrapGetStreamParametersReply(
216                          kOutput, device_id, std::move(on_params_callback)),
217                      base::nullopt));
218 }
219 
HasInputDevices(OnBoolCallback on_has_devices_callback)220 void AudioSystemToServiceAdapter::HasInputDevices(
221     OnBoolCallback on_has_devices_callback) {
222   GetSystemInfo()->HasInputDevices(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
223       WrapHasDevicesReply(kInput, std::move(on_has_devices_callback)), false));
224 }
225 
HasOutputDevices(OnBoolCallback on_has_devices_callback)226 void AudioSystemToServiceAdapter::HasOutputDevices(
227     OnBoolCallback on_has_devices_callback) {
228   GetSystemInfo()->HasOutputDevices(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
229       WrapHasDevicesReply(kOutput, std::move(on_has_devices_callback)), false));
230 }
231 
GetDeviceDescriptions(bool for_input,OnDeviceDescriptionsCallback on_descriptions_callback)232 void AudioSystemToServiceAdapter::GetDeviceDescriptions(
233     bool for_input,
234     OnDeviceDescriptionsCallback on_descriptions_callback) {
235   auto reply_callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
236       WrapCallbackWithDeviceNameLocalization(
237           std::move(on_descriptions_callback)),
238       media::AudioDeviceDescriptions());
239   if (for_input)
240     GetSystemInfo()->GetInputDeviceDescriptions(
241         WrapGetDeviceDescriptionsReply(kInput, std::move(reply_callback)));
242   else
243     GetSystemInfo()->GetOutputDeviceDescriptions(
244         WrapGetDeviceDescriptionsReply(kOutput, std::move(reply_callback)));
245 }
246 
GetAssociatedOutputDeviceID(const std::string & input_device_id,OnDeviceIdCallback on_device_id_callback)247 void AudioSystemToServiceAdapter::GetAssociatedOutputDeviceID(
248     const std::string& input_device_id,
249     OnDeviceIdCallback on_device_id_callback) {
250   GetSystemInfo()->GetAssociatedOutputDeviceID(
251       input_device_id,
252       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
253           WrapGetAssociatedOutputDeviceIDReply(
254               input_device_id, std::move(on_device_id_callback)),
255           base::nullopt));
256 }
257 
GetInputDeviceInfo(const std::string & input_device_id,OnInputDeviceInfoCallback on_input_device_info_callback)258 void AudioSystemToServiceAdapter::GetInputDeviceInfo(
259     const std::string& input_device_id,
260     OnInputDeviceInfoCallback on_input_device_info_callback) {
261   GetSystemInfo()->GetInputDeviceInfo(
262       input_device_id,
263       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
264           WrapGetInputDeviceInfoReply(input_device_id,
265                                       std::move(on_input_device_info_callback)),
266           base::nullopt, base::nullopt));
267 }
268 
GetSystemInfo()269 mojom::SystemInfo* AudioSystemToServiceAdapter::GetSystemInfo() {
270   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
271   if (!system_info_) {
272     TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
273         "audio", "AudioSystemToServiceAdapter bound", this);
274     system_info_binder_.Run(system_info_.BindNewPipeAndPassReceiver());
275     system_info_.set_disconnect_handler(
276         base::BindOnce(&AudioSystemToServiceAdapter::OnConnectionError,
277                        base::Unretained(this)));
278     if (!disconnect_timeout_.is_zero())
279       system_info_.reset_on_idle_timeout(disconnect_timeout_);
280   }
281 
282   return system_info_.get();
283 }
284 
OnConnectionError()285 void AudioSystemToServiceAdapter::OnConnectionError() {
286   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
287   TRACE_EVENT_NESTABLE_ASYNC_END1("audio", "AudioSystemToServiceAdapter bound",
288                                   this, "disconnect reason",
289                                   "connection error");
290   system_info_.reset();
291 }
292 
293 }  // namespace audio
294