1 // Copyright (c) 2012 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/media/webrtc/media_capture_devices_dispatcher.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/check_op.h"
12 #include "base/command_line.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "build/build_config.h"
18 #include "chrome/browser/media/media_access_handler.h"
19 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
20 #include "chrome/browser/media/webrtc/permission_bubble_media_access_handler.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/pref_names.h"
24 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "components/prefs/pref_service.h"
26 #include "components/prefs/scoped_user_pref_update.h"
27 #include "content/public/browser/browser_task_traits.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/media_capture_devices.h"
30 #include "content/public/browser/notification_source.h"
31 #include "content/public/browser/render_frame_host.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/browser/web_contents.h"
34 #include "extensions/buildflags/buildflags.h"
35 #include "extensions/common/constants.h"
36 #include "media/base/media_switches.h"
37 #include "third_party/blink/public/common/features.h"
38 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
39
40 #if defined(OS_ANDROID)
41 #include "content/public/common/content_features.h"
42 #else // !OS_ANDROID
43 #include "chrome/browser/media/webrtc/display_media_access_handler.h"
44 #endif // defined(OS_ANDROID)
45
46 #if defined(OS_CHROMEOS)
47 #include "ash/shell.h"
48 #include "chrome/browser/media/chromeos_login_media_access_handler.h"
49 #include "chrome/browser/media/public_session_media_access_handler.h"
50 #include "chrome/browser/media/public_session_tab_capture_access_handler.h"
51 #endif // defined(OS_CHROMEOS)
52
53 #if BUILDFLAG(ENABLE_EXTENSIONS)
54 #include "chrome/browser/media/extension_media_access_handler.h"
55 #include "chrome/browser/media/webrtc/desktop_capture_access_handler.h"
56 #include "chrome/browser/media/webrtc/tab_capture_access_handler.h"
57 #include "extensions/browser/extension_registry.h"
58 #include "extensions/common/extension.h"
59 #include "extensions/common/permissions/permissions_data.h"
60 #endif // BUILDFLAG(ENABLE_EXTENSIONS)
61
62 using blink::MediaStreamDevices;
63 using content::BrowserThread;
64 using content::MediaCaptureDevices;
65
66 namespace {
67
WebContentsFromIds(int render_process_id,int render_frame_id)68 content::WebContents* WebContentsFromIds(int render_process_id,
69 int render_frame_id) {
70 content::WebContents* web_contents =
71 content::WebContents::FromRenderFrameHost(
72 content::RenderFrameHost::FromID(render_process_id, render_frame_id));
73 return web_contents;
74 }
75
76 } // namespace
77
GetInstance()78 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
79 return base::Singleton<MediaCaptureDevicesDispatcher>::get();
80 }
81
MediaCaptureDevicesDispatcher()82 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
83 : is_device_enumeration_disabled_(false),
84 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) {
85 DCHECK_CURRENTLY_ON(BrowserThread::UI);
86
87 #if !defined(OS_ANDROID)
88 media_access_handlers_.push_back(
89 std::make_unique<DisplayMediaAccessHandler>());
90 #endif // defined(OS_ANDROID)
91
92 #if BUILDFLAG(ENABLE_EXTENSIONS)
93 #if defined(OS_CHROMEOS)
94 media_access_handlers_.push_back(
95 std::make_unique<ChromeOSLoginMediaAccessHandler>());
96 // Wrapper around ExtensionMediaAccessHandler used in Public Sessions.
97 media_access_handlers_.push_back(
98 std::make_unique<PublicSessionMediaAccessHandler>());
99 #else
100 media_access_handlers_.push_back(
101 std::make_unique<ExtensionMediaAccessHandler>());
102 #endif
103 media_access_handlers_.push_back(
104 std::make_unique<DesktopCaptureAccessHandler>());
105 #if defined(OS_CHROMEOS)
106 // Wrapper around TabCaptureAccessHandler used in Public Sessions.
107 media_access_handlers_.push_back(
108 std::make_unique<PublicSessionTabCaptureAccessHandler>());
109 #else
110 media_access_handlers_.push_back(std::make_unique<TabCaptureAccessHandler>());
111 #endif
112 #endif
113 media_access_handlers_.push_back(
114 std::make_unique<PermissionBubbleMediaAccessHandler>());
115 }
116
~MediaCaptureDevicesDispatcher()117 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
118
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)119 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
120 user_prefs::PrefRegistrySyncable* registry) {
121 registry->RegisterStringPref(prefs::kDefaultAudioCaptureDevice,
122 std::string());
123 registry->RegisterStringPref(prefs::kDefaultVideoCaptureDevice,
124 std::string());
125 }
126
IsOriginForCasting(const GURL & origin)127 bool MediaCaptureDevicesDispatcher::IsOriginForCasting(const GURL& origin) {
128 // Allowed tab casting extensions.
129 return
130 // Media Router Dev
131 origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" ||
132 // Media Router Stable
133 origin.spec() == "chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/";
134 }
135
AddObserver(Observer * observer)136 void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
137 DCHECK_CURRENTLY_ON(BrowserThread::UI);
138 if (!observers_.HasObserver(observer))
139 observers_.AddObserver(observer);
140 }
141
RemoveObserver(Observer * observer)142 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
143 DCHECK_CURRENTLY_ON(BrowserThread::UI);
144 observers_.RemoveObserver(observer);
145 }
146
ProcessMediaAccessRequest(content::WebContents * web_contents,const content::MediaStreamRequest & request,content::MediaResponseCallback callback,const extensions::Extension * extension)147 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
148 content::WebContents* web_contents,
149 const content::MediaStreamRequest& request,
150 content::MediaResponseCallback callback,
151 const extensions::Extension* extension) {
152 DCHECK_CURRENTLY_ON(BrowserThread::UI);
153
154 #if defined(OS_ANDROID)
155 // Kill switch for getDisplayMedia() on browser side to prevent renderer from
156 // bypassing blink side checks.
157 if (request.video_type ==
158 blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE &&
159 !base::FeatureList::IsEnabled(features::kUserMediaScreenCapturing)) {
160 std::move(callback).Run(
161 blink::MediaStreamDevices(),
162 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, nullptr);
163 return;
164 }
165 #endif
166
167 // Kill switch for getCurrentBrowsingContextMedia() on browser side to prevent
168 // renderer from bypassing blink side checks.
169 if (request.video_type ==
170 blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB) {
171 if (!base::FeatureList::IsEnabled(
172 blink::features::kRTCGetCurrentBrowsingContextMedia)) {
173 std::move(callback).Run(
174 blink::MediaStreamDevices(),
175 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, nullptr);
176 return;
177 }
178 }
179
180 for (const auto& handler : media_access_handlers_) {
181 if (handler->SupportsStreamType(web_contents, request.video_type,
182 extension) ||
183 handler->SupportsStreamType(web_contents, request.audio_type,
184 extension)) {
185 handler->HandleRequest(web_contents, request, std::move(callback),
186 extension);
187 return;
188 }
189 }
190 std::move(callback).Run(blink::MediaStreamDevices(),
191 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
192 nullptr);
193 }
194
CheckMediaAccessPermission(content::RenderFrameHost * render_frame_host,const GURL & security_origin,blink::mojom::MediaStreamType type)195 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
196 content::RenderFrameHost* render_frame_host,
197 const GURL& security_origin,
198 blink::mojom::MediaStreamType type) {
199 DCHECK_CURRENTLY_ON(BrowserThread::UI);
200 return CheckMediaAccessPermission(render_frame_host, security_origin, type,
201 nullptr);
202 }
203
CheckMediaAccessPermission(content::RenderFrameHost * render_frame_host,const GURL & security_origin,blink::mojom::MediaStreamType type,const extensions::Extension * extension)204 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
205 content::RenderFrameHost* render_frame_host,
206 const GURL& security_origin,
207 blink::mojom::MediaStreamType type,
208 const extensions::Extension* extension) {
209 DCHECK_CURRENTLY_ON(BrowserThread::UI);
210 for (const auto& handler : media_access_handlers_) {
211 if (handler->SupportsStreamType(
212 content::WebContents::FromRenderFrameHost(render_frame_host), type,
213 extension)) {
214 return handler->CheckMediaAccessPermission(
215 render_frame_host, security_origin, type, extension);
216 }
217 }
218 return false;
219 }
220
DisableDeviceEnumerationForTesting()221 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
222 is_device_enumeration_disabled_ = true;
223 }
224
GetDefaultDeviceIDForProfile(Profile * profile,blink::mojom::MediaStreamType type)225 std::string MediaCaptureDevicesDispatcher::GetDefaultDeviceIDForProfile(
226 Profile* profile,
227 blink::mojom::MediaStreamType type) {
228 DCHECK_CURRENTLY_ON(BrowserThread::UI);
229 PrefService* prefs = profile->GetPrefs();
230 if (type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE)
231 return prefs->GetString(prefs::kDefaultAudioCaptureDevice);
232 else if (type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)
233 return prefs->GetString(prefs::kDefaultVideoCaptureDevice);
234 else
235 return std::string();
236 }
237
238 const MediaStreamDevices&
GetAudioCaptureDevices() const239 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() const {
240 DCHECK_CURRENTLY_ON(BrowserThread::UI);
241 if (is_device_enumeration_disabled_ || !test_audio_devices_.empty())
242 return test_audio_devices_;
243
244 return MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
245 }
246
247 const MediaStreamDevices&
GetVideoCaptureDevices() const248 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() const {
249 DCHECK_CURRENTLY_ON(BrowserThread::UI);
250 if (is_device_enumeration_disabled_ || !test_video_devices_.empty())
251 return test_video_devices_;
252
253 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
254 }
255
GetDefaultDevicesForBrowserContext(content::BrowserContext * context,bool audio,bool video,blink::MediaStreamDevices * devices)256 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForBrowserContext(
257 content::BrowserContext* context,
258 bool audio,
259 bool video,
260 blink::MediaStreamDevices* devices) {
261 DCHECK_CURRENTLY_ON(BrowserThread::UI);
262 DCHECK(audio || video);
263
264 PrefService* prefs = Profile::FromBrowserContext(context)->GetPrefs();
265 std::string default_device;
266 if (audio) {
267 default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
268 const blink::MediaStreamDevice* device =
269 GetRequestedAudioDevice(default_device);
270 if (device) {
271 devices->push_back(*device);
272 } else {
273 const blink::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
274 if (!audio_devices.empty())
275 devices->push_back(audio_devices.front());
276 }
277 }
278
279 if (video) {
280 default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
281 const blink::MediaStreamDevice* device =
282 GetRequestedVideoDevice(default_device);
283 if (device) {
284 devices->push_back(*device);
285 } else {
286 const blink::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
287 if (!video_devices.empty())
288 devices->push_back(video_devices.front());
289 }
290 }
291 }
292
293 #if 0
294 const blink::MediaStreamDevice*
295 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
296 const std::string& requested_audio_device_id) {
297 DCHECK_CURRENTLY_ON(BrowserThread::UI);
298 const blink::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
299 const blink::MediaStreamDevice* const device =
300 FindDeviceWithId(audio_devices, requested_audio_device_id);
301 return device;
302 }
303
304 const blink::MediaStreamDevice*
305 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
306 const std::string& requested_video_device_id) {
307 DCHECK_CURRENTLY_ON(BrowserThread::UI);
308 const blink::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
309 const blink::MediaStreamDevice* const device =
310 FindDeviceWithId(video_devices, requested_video_device_id);
311 return device;
312 }
313 #endif
314
315 scoped_refptr<MediaStreamCaptureIndicator>
GetMediaStreamCaptureIndicator()316 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
317 return media_stream_capture_indicator_;
318 }
319
OnAudioCaptureDevicesChanged()320 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {
321 DCHECK_CURRENTLY_ON(BrowserThread::IO);
322 content::GetUIThreadTaskRunner({})->PostTask(
323 FROM_HERE,
324 base::BindOnce(
325 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread,
326 base::Unretained(this)));
327 }
328
OnVideoCaptureDevicesChanged()329 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {
330 DCHECK_CURRENTLY_ON(BrowserThread::IO);
331 content::GetUIThreadTaskRunner({})->PostTask(
332 FROM_HERE,
333 base::BindOnce(
334 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread,
335 base::Unretained(this)));
336 }
337
OnMediaRequestStateChanged(int render_process_id,int render_frame_id,int page_request_id,const GURL & security_origin,blink::mojom::MediaStreamType stream_type,content::MediaRequestState state)338 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
339 int render_process_id,
340 int render_frame_id,
341 int page_request_id,
342 const GURL& security_origin,
343 blink::mojom::MediaStreamType stream_type,
344 content::MediaRequestState state) {
345 DCHECK_CURRENTLY_ON(BrowserThread::IO);
346 content::GetUIThreadTaskRunner({})->PostTask(
347 FROM_HERE,
348 base::BindOnce(
349 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
350 base::Unretained(this), render_process_id, render_frame_id,
351 page_request_id, security_origin, stream_type, state));
352 }
353
OnCreatingAudioStream(int render_process_id,int render_frame_id)354 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(int render_process_id,
355 int render_frame_id) {
356 // TODO(https://crbug.com/837606): Figure out how to simplify threading here.
357 // Currently, this will either always be called on the UI thread, or always
358 // on the IO thread, depending on how far along the work to migrate to the
359 // audio service has progressed. The rest of the methods of the
360 // content::MediaObserver are always called on the IO thread.
361 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
362 OnCreatingAudioStreamOnUIThread(render_process_id, render_frame_id);
363 return;
364 }
365
366 DCHECK_CURRENTLY_ON(BrowserThread::IO);
367 content::GetUIThreadTaskRunner({})->PostTask(
368 FROM_HERE,
369 base::BindOnce(
370 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread,
371 base::Unretained(this), render_process_id, render_frame_id));
372 }
373
NotifyAudioDevicesChangedOnUIThread()374 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
375 MediaStreamDevices devices = GetAudioCaptureDevices();
376 for (auto& observer : observers_)
377 observer.OnUpdateAudioDevices(devices);
378 }
379
NotifyVideoDevicesChangedOnUIThread()380 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
381 MediaStreamDevices devices = GetVideoCaptureDevices();
382 for (auto& observer : observers_)
383 observer.OnUpdateVideoDevices(devices);
384 }
385
UpdateMediaRequestStateOnUIThread(int render_process_id,int render_frame_id,int page_request_id,const GURL & security_origin,blink::mojom::MediaStreamType stream_type,content::MediaRequestState state)386 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
387 int render_process_id,
388 int render_frame_id,
389 int page_request_id,
390 const GURL& security_origin,
391 blink::mojom::MediaStreamType stream_type,
392 content::MediaRequestState state) {
393 DCHECK_CURRENTLY_ON(BrowserThread::UI);
394 for (const auto& handler : media_access_handlers_) {
395 if (handler->SupportsStreamType(
396 WebContentsFromIds(render_process_id, render_frame_id), stream_type,
397 nullptr)) {
398 handler->UpdateMediaRequestState(render_process_id, render_frame_id,
399 page_request_id, stream_type, state);
400 break;
401 }
402 }
403
404 #if defined(OS_CHROMEOS)
405 if (IsOriginForCasting(security_origin) &&
406 blink::IsVideoInputMediaType(stream_type)) {
407 // Notify ash that casting state has changed.
408 if (state == content::MEDIA_REQUEST_STATE_DONE) {
409 ash::Shell::Get()->OnCastingSessionStartedOrStopped(true);
410 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
411 ash::Shell::Get()->OnCastingSessionStartedOrStopped(false);
412 }
413 }
414 #endif
415
416 for (auto& observer : observers_) {
417 observer.OnRequestUpdate(render_process_id, render_frame_id, stream_type,
418 state);
419 }
420 }
421
OnCreatingAudioStreamOnUIThread(int render_process_id,int render_frame_id)422 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
423 int render_process_id,
424 int render_frame_id) {
425 DCHECK_CURRENTLY_ON(BrowserThread::UI);
426 for (auto& observer : observers_)
427 observer.OnCreatingAudioStream(render_process_id, render_frame_id);
428 }
429
IsInsecureCapturingInProgress(int render_process_id,int render_frame_id)430 bool MediaCaptureDevicesDispatcher::IsInsecureCapturingInProgress(
431 int render_process_id,
432 int render_frame_id) {
433 DCHECK_CURRENTLY_ON(BrowserThread::UI);
434
435 for (const auto& handler : media_access_handlers_) {
436 if (handler->IsInsecureCapturingInProgress(render_process_id,
437 render_frame_id))
438 return true;
439 }
440 return false;
441 }
442
SetTestAudioCaptureDevices(const MediaStreamDevices & devices)443 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
444 const MediaStreamDevices& devices) {
445 test_audio_devices_ = devices;
446 }
447
SetTestVideoCaptureDevices(const MediaStreamDevices & devices)448 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
449 const MediaStreamDevices& devices) {
450 test_video_devices_ = devices;
451 }
452
OnSetCapturingLinkSecured(int render_process_id,int render_frame_id,int page_request_id,blink::mojom::MediaStreamType stream_type,bool is_secure)453 void MediaCaptureDevicesDispatcher::OnSetCapturingLinkSecured(
454 int render_process_id,
455 int render_frame_id,
456 int page_request_id,
457 blink::mojom::MediaStreamType stream_type,
458 bool is_secure) {
459 DCHECK_CURRENTLY_ON(BrowserThread::IO);
460
461 if (!blink::IsVideoScreenCaptureMediaType(stream_type))
462 return;
463
464 content::GetUIThreadTaskRunner({})->PostTask(
465 FROM_HERE,
466 base::BindOnce(
467 &MediaCaptureDevicesDispatcher::UpdateVideoScreenCaptureStatus,
468 base::Unretained(this), render_process_id, render_frame_id,
469 page_request_id, stream_type, is_secure));
470 }
471
UpdateVideoScreenCaptureStatus(int render_process_id,int render_frame_id,int page_request_id,blink::mojom::MediaStreamType stream_type,bool is_secure)472 void MediaCaptureDevicesDispatcher::UpdateVideoScreenCaptureStatus(
473 int render_process_id,
474 int render_frame_id,
475 int page_request_id,
476 blink::mojom::MediaStreamType stream_type,
477 bool is_secure) {
478 DCHECK_CURRENTLY_ON(BrowserThread::UI);
479 DCHECK(blink::IsVideoScreenCaptureMediaType(stream_type));
480
481 for (const auto& handler : media_access_handlers_) {
482 if (handler->SupportsStreamType(
483 WebContentsFromIds(render_process_id, render_frame_id), stream_type,
484 nullptr)) {
485 handler->UpdateVideoScreenCaptureStatus(
486 render_process_id, render_frame_id, page_request_id, is_secure);
487 break;
488 }
489 }
490 }
491