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