1 // Copyright 2013 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 "chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/lazy_instance.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/task_runner_util.h"
16 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
17 #include "chrome/browser/extensions/extension_tab_util.h"
18 #include "content/public/browser/audio_service.h"
19 #include "content/public/browser/browser_context.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/media_device_id.h"
22 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/web_contents.h"
24 #include "extensions/browser/event_router.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/common/error_utils.h"
27 #include "extensions/common/permissions/permissions_data.h"
28 #include "media/audio/audio_system.h"
29 #include "url/gurl.h"
30 #include "url/origin.h"
31
32 namespace extensions {
33
34 using content::BrowserThread;
35 namespace wap = api::webrtc_audio_private;
36
37 static base::LazyInstance<BrowserContextKeyedAPIFactory<
38 WebrtcAudioPrivateEventService>>::DestructorAtExit
39 g_webrtc_audio_private_api_factory = LAZY_INSTANCE_INITIALIZER;
40
WebrtcAudioPrivateEventService(content::BrowserContext * context)41 WebrtcAudioPrivateEventService::WebrtcAudioPrivateEventService(
42 content::BrowserContext* context)
43 : browser_context_(context) {
44 // In unit tests, the SystemMonitor may not be created.
45 base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
46 if (system_monitor)
47 system_monitor->AddDevicesChangedObserver(this);
48 }
49
~WebrtcAudioPrivateEventService()50 WebrtcAudioPrivateEventService::~WebrtcAudioPrivateEventService() {
51 }
52
Shutdown()53 void WebrtcAudioPrivateEventService::Shutdown() {
54 // In unit tests, the SystemMonitor may not be created.
55 base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
56 if (system_monitor)
57 system_monitor->RemoveDevicesChangedObserver(this);
58 }
59
60 // static
61 BrowserContextKeyedAPIFactory<WebrtcAudioPrivateEventService>*
GetFactoryInstance()62 WebrtcAudioPrivateEventService::GetFactoryInstance() {
63 return g_webrtc_audio_private_api_factory.Pointer();
64 }
65
66 // static
service_name()67 const char* WebrtcAudioPrivateEventService::service_name() {
68 return "WebrtcAudioPrivateEventService";
69 }
70
OnDevicesChanged(base::SystemMonitor::DeviceType device_type)71 void WebrtcAudioPrivateEventService::OnDevicesChanged(
72 base::SystemMonitor::DeviceType device_type) {
73 switch (device_type) {
74 case base::SystemMonitor::DEVTYPE_AUDIO:
75 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
76 SignalEvent();
77 break;
78 default:
79 // No action needed.
80 break;
81 }
82 }
83
SignalEvent()84 void WebrtcAudioPrivateEventService::SignalEvent() {
85 using api::webrtc_audio_private::OnSinksChanged::kEventName;
86
87 EventRouter* router = EventRouter::Get(browser_context_);
88 if (!router || !router->HasEventListener(kEventName))
89 return;
90
91 for (const scoped_refptr<const extensions::Extension>& extension :
92 ExtensionRegistry::Get(browser_context_)->enabled_extensions()) {
93 const std::string& extension_id = extension->id();
94 if (router->ExtensionHasEventListener(extension_id, kEventName) &&
95 extension->permissions_data()->HasAPIPermission("webrtcAudioPrivate")) {
96 std::unique_ptr<Event> event = std::make_unique<Event>(
97 events::WEBRTC_AUDIO_PRIVATE_ON_SINKS_CHANGED, kEventName,
98 std::make_unique<base::ListValue>());
99 router->DispatchEventToExtension(extension_id, std::move(event));
100 }
101 }
102 }
103
WebrtcAudioPrivateFunction()104 WebrtcAudioPrivateFunction::WebrtcAudioPrivateFunction() {}
105
~WebrtcAudioPrivateFunction()106 WebrtcAudioPrivateFunction::~WebrtcAudioPrivateFunction() {}
107
CalculateHMAC(const std::string & raw_id)108 std::string WebrtcAudioPrivateFunction::CalculateHMAC(
109 const std::string& raw_id) {
110 DCHECK_CURRENTLY_ON(BrowserThread::UI);
111
112 // We don't hash the default device description, and we always return
113 // "default" for the default device. There is code in SetActiveSink
114 // that transforms "default" to the empty string, and code in
115 // GetActiveSink that ensures we return "default" if we get the
116 // empty string as the current device ID.
117 if (media::AudioDeviceDescription::IsDefaultDevice(raw_id))
118 return media::AudioDeviceDescription::kDefaultDeviceId;
119
120 url::Origin security_origin = url::Origin::Create(source_url().GetOrigin());
121 return content::GetHMACForMediaDeviceID(device_id_salt(), security_origin,
122 raw_id);
123 }
124
InitDeviceIDSalt()125 void WebrtcAudioPrivateFunction::InitDeviceIDSalt() {
126 device_id_salt_ = browser_context()->GetMediaDeviceIDSalt();
127 }
128
device_id_salt() const129 std::string WebrtcAudioPrivateFunction::device_id_salt() const {
130 return device_id_salt_;
131 }
132
GetAudioSystem()133 media::AudioSystem* WebrtcAudioPrivateFunction::GetAudioSystem() {
134 DCHECK_CURRENTLY_ON(BrowserThread::UI);
135 if (!audio_system_)
136 audio_system_ = content::CreateAudioSystemForAudioService();
137 return audio_system_.get();
138 }
139
Run()140 ExtensionFunction::ResponseAction WebrtcAudioPrivateGetSinksFunction::Run() {
141 DCHECK_CURRENTLY_ON(BrowserThread::UI);
142 InitDeviceIDSalt();
143 GetAudioSystem()->GetDeviceDescriptions(
144 false,
145 base::BindOnce(
146 &WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions,
147 this));
148 return RespondLater();
149 }
150
ReceiveOutputDeviceDescriptions(media::AudioDeviceDescriptions sink_devices)151 void WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions(
152 media::AudioDeviceDescriptions sink_devices) {
153 DCHECK_CURRENTLY_ON(BrowserThread::UI);
154 auto results = std::make_unique<SinkInfoVector>();
155 for (const media::AudioDeviceDescription& description : sink_devices) {
156 wap::SinkInfo info;
157 info.sink_id = CalculateHMAC(description.unique_id);
158 info.sink_label = description.device_name;
159 // TODO(joi): Add other parameters.
160 results->push_back(std::move(info));
161 }
162 Respond(ArgumentList(wap::GetSinks::Results::Create(*results)));
163 }
164
165 WebrtcAudioPrivateGetAssociatedSinkFunction::
WebrtcAudioPrivateGetAssociatedSinkFunction()166 WebrtcAudioPrivateGetAssociatedSinkFunction() {}
167
168 WebrtcAudioPrivateGetAssociatedSinkFunction::
~WebrtcAudioPrivateGetAssociatedSinkFunction()169 ~WebrtcAudioPrivateGetAssociatedSinkFunction() {}
170
171 ExtensionFunction::ResponseAction
Run()172 WebrtcAudioPrivateGetAssociatedSinkFunction::Run() {
173 params_ = wap::GetAssociatedSink::Params::Create(*args_);
174 DCHECK_CURRENTLY_ON(BrowserThread::UI);
175 EXTENSION_FUNCTION_VALIDATE(params_.get());
176 InitDeviceIDSalt();
177
178 GetAudioSystem()->GetDeviceDescriptions(
179 true, base::BindOnce(&WebrtcAudioPrivateGetAssociatedSinkFunction::
180 ReceiveInputDeviceDescriptions,
181 this));
182 return RespondLater();
183 }
184
185 void WebrtcAudioPrivateGetAssociatedSinkFunction::
ReceiveInputDeviceDescriptions(media::AudioDeviceDescriptions source_devices)186 ReceiveInputDeviceDescriptions(
187 media::AudioDeviceDescriptions source_devices) {
188 DCHECK_CURRENTLY_ON(BrowserThread::UI);
189 url::Origin security_origin =
190 url::Origin::Create(GURL(params_->security_origin));
191 std::string source_id_in_origin(params_->source_id_in_origin);
192
193 // Find the raw source ID for source_id_in_origin.
194 std::string raw_source_id;
195 for (const auto& device : source_devices) {
196 if (content::DoesMediaDeviceIDMatchHMAC(device_id_salt(), security_origin,
197 source_id_in_origin,
198 device.unique_id)) {
199 raw_source_id = device.unique_id;
200 DVLOG(2) << "Found raw ID " << raw_source_id
201 << " for source ID in origin " << source_id_in_origin;
202 break;
203 }
204 }
205 if (raw_source_id.empty()) {
206 CalculateHMACAndReply(base::nullopt);
207 return;
208 }
209 GetAudioSystem()->GetAssociatedOutputDeviceID(
210 raw_source_id,
211 base::BindOnce(
212 &WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACAndReply,
213 this));
214 }
215
CalculateHMACAndReply(const base::Optional<std::string> & raw_sink_id)216 void WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACAndReply(
217 const base::Optional<std::string>& raw_sink_id) {
218 DCHECK_CURRENTLY_ON(BrowserThread::UI);
219 DCHECK(!raw_sink_id || !raw_sink_id->empty());
220 // If no |raw_sink_id| is provided, the default device is used.
221 Reply(CalculateHMAC(raw_sink_id.value_or(std::string())));
222 }
223
Reply(const std::string & associated_sink_id)224 void WebrtcAudioPrivateGetAssociatedSinkFunction::Reply(
225 const std::string& associated_sink_id) {
226 DCHECK_CURRENTLY_ON(BrowserThread::UI);
227 std::string sink_id;
228 if (associated_sink_id == media::AudioDeviceDescription::kDefaultDeviceId) {
229 DVLOG(2) << "Got default ID, replacing with empty ID.";
230 } else {
231 sink_id = associated_sink_id;
232 }
233 Respond(OneArgument(base::Value(sink_id)));
234 }
235
236 } // namespace extensions
237