[M120 Migration] Build libchromium-impl.so with chrome implementation
[platform/framework/web/chromium-efl.git] / content / browser / renderer_host / media / media_devices_manager.cc
1 // Copyright 2016 The Chromium Authors
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 "content/browser/renderer_host/media/media_devices_manager.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <functional>
11 #include <map>
12 #include <string>
13
14 #include "base/command_line.h"
15 #include "base/containers/contains.h"
16 #include "base/containers/cxx20_erase.h"
17 #include "base/functional/bind.h"
18 #include "base/location.h"
19 #include "base/metrics/histogram_functions.h"
20 #include "base/ranges/algorithm.h"
21 #include "base/sequence_checker.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/task/bind_post_task.h"
24 #include "base/task/sequenced_task_runner.h"
25 #include "base/task/single_thread_task_runner.h"
26 #include "base/threading/thread_checker.h"
27 #include "build/build_config.h"
28 #include "build/chromeos_buildflags.h"
29 #include "content/browser/media/media_devices_permission_checker.h"
30 #include "content/browser/renderer_host/media/media_stream_manager.h"
31 #include "content/browser/renderer_host/media/video_capture_manager.h"
32 #include "content/common/features.h"
33 #include "content/public/browser/audio_service.h"
34 #include "content/public/browser/browser_task_traits.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/common/content_features.h"
37 #include "media/audio/audio_device_description.h"
38 #include "media/audio/audio_system.h"
39 #include "media/base/media_switches.h"
40 #include "mojo/public/cpp/bindings/receiver.h"
41 #include "mojo/public/cpp/bindings/remote.h"
42 #include "services/audio/public/mojom/device_notifications.mojom.h"
43 #include "third_party/blink/public/common/mediastream/media_devices.h"
44 #include "third_party/blink/public/mojom/mediastream/media_devices.mojom.h"
45
46 #if BUILDFLAG(IS_MAC)
47 #include "base/functional/callback_helpers.h"
48 #include "base/task/single_thread_task_runner.h"
49 #include "content/browser/browser_main_loop.h"
50 #include "media/device_monitors/device_monitor_mac.h"
51 #endif
52
53 #if BUILDFLAG(IS_TIZEN_TV)
54 #include "tizen_src/ewk/efl_integration/common/application_type.h"
55 #endif
56
57 namespace content {
58
59 namespace {
60 using media::mojom::DeviceEnumerationResult;
61
62 // Resolutions used if the source doesn't support capability enumeration.
63 struct {
64   uint16_t width;
65   uint16_t height;
66 } const kFallbackVideoResolutions[] = {{1920, 1080}, {1280, 720}, {640, 480}};
67
68 // Frame rates for sources with no support for capability enumeration.
69 const uint16_t kFallbackVideoFrameRates[] = {30, 60};
70
71 void SendLogMessage(const std::string& message) {
72   MediaStreamManager::SendMessageToNativeLog("MDM::" + message);
73 }
74
75 const char* DeviceTypeToString(MediaDeviceType device_type) {
76   switch (device_type) {
77     case MediaDeviceType::kMediaAudioInput:
78       return "AUDIO_INPUT";
79     case MediaDeviceType::kMediaVideoInput:
80       return "VIDEO_INPUT";
81     case MediaDeviceType::kMediaAudioOuput:
82       return "AUDIO_OUTPUT";
83     default:
84       NOTREACHED();
85   }
86   return "UNKNOWN";
87 }
88
89 std::string GetDevicesEnumeratedLogString(
90     MediaDeviceType device_type,
91     const blink::WebMediaDeviceInfoArray& device_infos) {
92   std::string str = base::StringPrintf("DevicesEnumerated({type=%s}, ",
93                                        DeviceTypeToString(device_type));
94   base::StringAppendF(&str, "{labels=[");
95   for (const auto& device_info : device_infos)
96     base::StringAppendF(&str, "%s, ", device_info.label.c_str());
97   if (!str.empty()) {
98     str.erase(str.end() - 2, str.end());
99   }
100   str += "])";
101   return str;
102 }
103
104 blink::WebMediaDeviceInfoArray GetFakeAudioDevices(bool is_input) {
105   blink::WebMediaDeviceInfoArray result;
106   if (is_input) {
107     result.emplace_back(media::AudioDeviceDescription::kDefaultDeviceId,
108                         "Fake Default Audio Input",
109                         "fake_group_audio_input_default");
110     result.emplace_back("fake_audio_input_1", "Fake Audio Input 1",
111                         "fake_group_audio_input_1");
112     result.emplace_back("fake_audio_input_2", "Fake Audio Input 2",
113                         "fake_group_audio_input_2");
114   } else {
115     result.emplace_back(media::AudioDeviceDescription::kDefaultDeviceId,
116                         "Fake Default Audio Output",
117                         "fake_group_audio_output_default");
118     result.emplace_back("fake_audio_output_1", "Fake Audio Output 1",
119                         "fake_group_audio_output_1");
120     result.emplace_back("fake_audio_output_2", "Fake Audio Output 2",
121                         "fake_group_audio_output_2");
122   }
123
124   return result;
125 }
126
127 std::string VideoLabelWithoutModelID(const std::string& label) {
128   if (label.rfind(")") != label.size() - 1)
129     return label;
130
131   auto idx = label.rfind(" (");
132   if (idx == std::string::npos)
133     return label;
134
135   return label.substr(0, idx - 1);
136 }
137
138 bool LabelHasUSBModel(const std::string& label) {
139   return label.size() >= 11 && label[label.size() - 11] == '(' &&
140          label[label.size() - 6] == ':' && label[label.size() - 1] == ')';
141 }
142
143 std::string GetUSBModelFromLabel(const std::string& label) {
144   DCHECK(LabelHasUSBModel(label));
145   return label.substr(label.size() - 10, 9);
146 }
147
148 bool IsRealAudioDeviceID(const std::string& device_id) {
149   return !media::AudioDeviceDescription::IsDefaultDevice(device_id) &&
150          !media::AudioDeviceDescription::IsCommunicationsDevice(device_id);
151 }
152
153 static bool EqualDeviceAndGroupID(const blink::WebMediaDeviceInfo& lhs,
154                                   const blink::WebMediaDeviceInfo& rhs) {
155   return lhs == rhs && lhs.group_id == rhs.group_id;
156 }
157
158 void ReplaceInvalidFrameRatesWithFallback(media::VideoCaptureFormats* formats) {
159   for (auto& format : *formats) {
160     if (format.frame_rate <= 0)
161       format.frame_rate = kFallbackVideoFrameRates[0];
162   }
163 }
164
165 void BindDeviceNotifierFromUIThread(
166     mojo::PendingReceiver<audio::mojom::DeviceNotifier> receiver) {
167   DCHECK_CURRENTLY_ON(BrowserThread::UI);
168   GetAudioService().BindDeviceNotifier(std::move(receiver));
169 }
170
171 void ReportVideoEnumerationStart() {
172   base::UmaHistogramBoolean(
173       "Media.MediaDevicesManager.VideoDeviceEnumeration.Start", true);
174 }
175
176 void ReportVideoEnumerationResult(DeviceEnumerationResult result_code) {
177   base::UmaHistogramEnumeration(
178       "Media.MediaDevicesManager.VideoDeviceEnumeration.Result", result_code);
179 }
180
181 }  // namespace
182
183 std::string GuessVideoGroupID(const blink::WebMediaDeviceInfoArray& audio_infos,
184                               const blink::WebMediaDeviceInfo& video_info) {
185   const std::string video_label = VideoLabelWithoutModelID(video_info.label);
186
187   // If |video_label| is very small, do not guess in order to avoid false
188   // positives.
189   if (video_label.size() <= 3)
190     return video_info.device_id;
191
192   base::RepeatingCallback<bool(const blink::WebMediaDeviceInfo&)>
193       video_label_is_included_in_audio_label = base::BindRepeating(
194           [](const std::string& video_label,
195              const blink::WebMediaDeviceInfo& audio_info) {
196             return audio_info.label.find(video_label) != std::string::npos;
197           },
198           std::cref(video_label));
199
200   const bool video_has_usb_model = LabelHasUSBModel(video_info.label);
201   std::string video_usb_model = video_has_usb_model
202                                     ? GetUSBModelFromLabel(video_info.label)
203                                     : std::string();
204   base::RepeatingCallback<bool(const blink::WebMediaDeviceInfo&)>
205       usb_model_matches = base::BindRepeating(
206           [](bool video_has_usb_model, const std::string& video_usb_model,
207              const blink::WebMediaDeviceInfo& audio_info) {
208             return video_has_usb_model && LabelHasUSBModel(audio_info.label)
209                        ? video_usb_model ==
210                              GetUSBModelFromLabel(audio_info.label)
211                        : false;
212           },
213           video_has_usb_model, std::cref(video_usb_model));
214
215   for (auto* callback :
216        {&video_label_is_included_in_audio_label, &usb_model_matches}) {
217     // The label for the default and communication audio devices may contain the
218     // same label as the real devices, so they should be ignored when trying to
219     // find unique matches.
220     auto real_device_matches =
221         [callback](const blink::WebMediaDeviceInfo& audio_info) {
222           return IsRealAudioDeviceID(audio_info.device_id) &&
223                  (*callback).Run(audio_info);
224         };
225     auto it_first = base::ranges::find_if(audio_infos, real_device_matches);
226     if (it_first == audio_infos.end())
227       continue;
228
229     auto it = it_first;
230     bool duplicate_found = false;
231     while ((it = std::find_if(it + 1, audio_infos.end(),
232                               real_device_matches)) != audio_infos.end()) {
233       // If there is more than one match, it is impossible to know which group
234       // ID is the correct one. This may occur if multiple devices of the same
235       // model are installed.
236       if (it->group_id != it_first->group_id) {
237         duplicate_found = true;
238         break;
239       }
240     }
241
242     if (!duplicate_found)
243       return it_first->group_id;
244   }
245
246   return video_info.device_id;
247 }
248
249 struct MediaDevicesManager::EnumerationRequest {
250   EnumerationRequest(const BoolDeviceTypes& requested_types,
251                      EnumerationCallback callback)
252       : callback(std::move(callback)) {
253     requested = requested_types;
254     has_seen_result.fill(false);
255   }
256
257   BoolDeviceTypes requested;
258   BoolDeviceTypes has_seen_result;
259   EnumerationCallback callback;
260 };
261
262 // This class helps manage the consistency of cached enumeration results.
263 // It uses a sequence number for each invalidation and enumeration.
264 // A cache is considered valid if the sequence number for the last enumeration
265 // is greater than the sequence number for the last invalidation.
266 // The advantage of using invalidations over directly issuing enumerations upon
267 // each system notification is that some platforms issue multiple notifications
268 // on each device change. The cost of performing multiple redundant
269 // invalidations is significantly lower than the cost of issuing multiple
270 // redundant enumerations.
271 class MediaDevicesManager::CacheInfo {
272  public:
273   CacheInfo()
274       : current_event_sequence_(0),
275         seq_last_update_(0),
276         seq_last_invalidation_(0),
277         is_update_ongoing_(false) {}
278
279   void InvalidateCache() {
280     DCHECK(thread_checker_.CalledOnValidThread());
281     seq_last_invalidation_ = NewEventSequence();
282   }
283
284   bool IsLastUpdateValid() const {
285     DCHECK(thread_checker_.CalledOnValidThread());
286     return seq_last_update_ > seq_last_invalidation_ && !is_update_ongoing_;
287   }
288
289   void UpdateStarted() {
290     DCHECK(thread_checker_.CalledOnValidThread());
291     DCHECK(!is_update_ongoing_);
292     seq_last_update_ = NewEventSequence();
293     is_update_ongoing_ = true;
294   }
295
296   void UpdateCompleted() {
297     DCHECK(thread_checker_.CalledOnValidThread());
298     DCHECK(is_update_ongoing_);
299     is_update_ongoing_ = false;
300   }
301
302   bool is_update_ongoing() const {
303     DCHECK(thread_checker_.CalledOnValidThread());
304     return is_update_ongoing_;
305   }
306
307  private:
308   int64_t NewEventSequence() {
309     DCHECK(thread_checker_.CalledOnValidThread());
310     return ++current_event_sequence_;
311   }
312
313   int64_t current_event_sequence_;
314   int64_t seq_last_update_;
315   int64_t seq_last_invalidation_;
316   bool is_update_ongoing_;
317   base::ThreadChecker thread_checker_;
318 };
319
320 MediaDevicesManager::SubscriptionRequest::SubscriptionRequest(
321     int render_process_id,
322     int render_frame_id,
323     const BoolDeviceTypes& subscribe_types,
324     mojo::Remote<blink::mojom::MediaDevicesListener> listener)
325     : render_process_id(render_process_id),
326       render_frame_id(render_frame_id),
327       subscribe_types(subscribe_types),
328       listener_(std::move(listener)) {}
329
330 MediaDevicesManager::SubscriptionRequest::SubscriptionRequest(
331     SubscriptionRequest&&) = default;
332
333 MediaDevicesManager::SubscriptionRequest::~SubscriptionRequest() = default;
334
335 MediaDevicesManager::SubscriptionRequest&
336 MediaDevicesManager::SubscriptionRequest::operator=(SubscriptionRequest&&) =
337     default;
338
339 class MediaDevicesManager::AudioServiceDeviceListener
340     : public audio::mojom::DeviceListener {
341  public:
342   AudioServiceDeviceListener() { ConnectToService(); }
343
344   AudioServiceDeviceListener(const AudioServiceDeviceListener&) = delete;
345   AudioServiceDeviceListener& operator=(const AudioServiceDeviceListener&) =
346       delete;
347
348   ~AudioServiceDeviceListener() override = default;
349
350   void DevicesChanged() override {
351     auto* system_monitor = base::SystemMonitor::Get();
352     if (system_monitor)
353       system_monitor->ProcessDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO);
354   }
355
356  private:
357   void ConnectToService() {
358     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
359     DCHECK(!mojo_audio_device_notifier_);
360     DCHECK(!receiver_.is_bound());
361     GetUIThreadTaskRunner({})->PostTask(
362         FROM_HERE,
363         base::BindOnce(
364             &BindDeviceNotifierFromUIThread,
365             mojo_audio_device_notifier_.BindNewPipeAndPassReceiver()));
366     mojo_audio_device_notifier_.set_disconnect_handler(base::BindOnce(
367         &MediaDevicesManager::AudioServiceDeviceListener::OnConnectionError,
368         weak_factory_.GetWeakPtr()));
369     mojo_audio_device_notifier_->RegisterListener(
370         receiver_.BindNewPipeAndPassRemote());
371   }
372
373   void OnConnectionError() {
374     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
375     mojo_audio_device_notifier_.reset();
376     receiver_.reset();
377
378     // Resetting the error handler in a posted task since doing it synchronously
379     // results in a browser crash. See https://crbug.com/845142.
380     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
381         FROM_HERE, base::BindOnce(&AudioServiceDeviceListener::ConnectToService,
382                                   weak_factory_.GetWeakPtr()));
383   }
384
385   mojo::Receiver<audio::mojom::DeviceListener> receiver_{this};
386   mojo::Remote<audio::mojom::DeviceNotifier> mojo_audio_device_notifier_;
387
388   SEQUENCE_CHECKER(sequence_checker_);
389
390   base::WeakPtrFactory<AudioServiceDeviceListener> weak_factory_{this};
391 };
392
393 MediaDevicesManager::MediaDevicesManager(
394     media::AudioSystem* audio_system,
395     const scoped_refptr<VideoCaptureManager>& video_capture_manager,
396     StopRemovedInputDeviceCallback stop_removed_input_device_cb,
397     UIInputDeviceChangeCallback ui_input_device_change_cb)
398     : use_fake_devices_(base::CommandLine::ForCurrentProcess()->HasSwitch(
399           switches::kUseFakeDeviceForMediaStream)),
400       audio_system_(audio_system),
401       video_capture_manager_(video_capture_manager),
402       stop_removed_input_device_cb_(std::move(stop_removed_input_device_cb)),
403       ui_input_device_change_cb_(std::move(ui_input_device_change_cb)),
404       permission_checker_(std::make_unique<MediaDevicesPermissionChecker>()),
405       cache_infos_(static_cast<size_t>(MediaDeviceType::kNumMediaDeviceTypes)),
406       monitoring_started_(false),
407       get_salt_and_origin_cb_(
408           base::BindRepeating(&GetMediaDeviceSaltAndOrigin)) {
409   DCHECK_CURRENTLY_ON(BrowserThread::IO);
410   DCHECK(audio_system_);
411   DCHECK(video_capture_manager_.get());
412   DCHECK(!stop_removed_input_device_cb_.is_null());
413   DCHECK(!ui_input_device_change_cb_.is_null());
414   SendLogMessage("MediaDevicesManager()");
415   cache_policies_.fill(CachePolicy::NO_CACHE);
416   has_seen_result_.fill(false);
417 }
418
419 MediaDevicesManager::~MediaDevicesManager() {
420   DCHECK_CURRENTLY_ON(BrowserThread::IO);
421 }
422
423 void MediaDevicesManager::EnumerateDevices(
424     const BoolDeviceTypes& requested_types,
425     EnumerationCallback callback) {
426   DCHECK_CURRENTLY_ON(BrowserThread::IO);
427   StartMonitoring();
428
429   requests_.emplace_back(requested_types, std::move(callback));
430   bool all_results_cached = true;
431   for (size_t i = 0;
432        i < static_cast<size_t>(MediaDeviceType::kNumMediaDeviceTypes); ++i) {
433     if (requested_types[i] && cache_policies_[i] == CachePolicy::NO_CACHE) {
434       all_results_cached = false;
435       DoEnumerateDevices(static_cast<MediaDeviceType>(i));
436     }
437   }
438
439   if (all_results_cached)
440     ProcessRequests();
441 }
442
443 void MediaDevicesManager::EnumerateDevices(
444     int render_process_id,
445     int render_frame_id,
446     const BoolDeviceTypes& requested_types,
447     bool request_video_input_capabilities,
448     bool request_audio_input_capabilities,
449     EnumerateDevicesCallback callback) {
450   DCHECK_CURRENTLY_ON(BrowserThread::IO);
451   DCHECK(request_video_input_capabilities &&
452              requested_types[static_cast<size_t>(
453                  MediaDeviceType::kMediaVideoInput)] ||
454          !request_video_input_capabilities);
455   DCHECK(request_audio_input_capabilities &&
456              requested_types[static_cast<size_t>(
457                  MediaDeviceType::kMediaAudioInput)] ||
458          !request_audio_input_capabilities);
459   SendLogMessage(base::StringPrintf(
460       "EnumerateDevices({render_process_id=%d}, {render_frame_id=%d}, "
461       "{request_audio=%s}, {request_video=%s})",
462       render_process_id, render_frame_id,
463       request_audio_input_capabilities ? "true" : "false",
464       request_video_input_capabilities ? "true" : "false"));
465
466   GetUIThreadTaskRunner({})->PostTask(
467       FROM_HERE,
468       base::BindOnce(
469           get_salt_and_origin_cb_,
470           GlobalRenderFrameHostId(render_process_id, render_frame_id),
471           base::BindPostTaskToCurrentDefault(base::BindOnce(
472               &MediaDevicesManager::CheckPermissionsForEnumerateDevices,
473               weak_factory_.GetWeakPtr(), render_process_id, render_frame_id,
474               requested_types, request_video_input_capabilities,
475               request_audio_input_capabilities, std::move(callback)))));
476 }
477
478 #if BUILDFLAG(IS_TIZEN_TV)
479 void MediaDevicesManager::GetMediaDeviceList(EnumerationCallback cb) {
480   DCHECK_CURRENTLY_ON(BrowserThread::IO);
481   enum_cb_ = std::move(cb);
482   got_result_.fill(false);
483   for (auto& device : device_infos_)
484     device.clear();
485
486   for (size_t i = 0;
487        i < static_cast<size_t>(MediaDeviceType::kNumMediaDeviceTypes); ++i) {
488     DoEnumerateDevices(static_cast<MediaDeviceType>(i));
489   }
490 }
491 #endif
492
493 uint32_t MediaDevicesManager::SubscribeDeviceChangeNotifications(
494     int render_process_id,
495     int render_frame_id,
496     const BoolDeviceTypes& subscribe_types,
497     mojo::PendingRemote<blink::mojom::MediaDevicesListener> listener) {
498   DCHECK_CURRENTLY_ON(BrowserThread::IO);
499   StartMonitoring();
500   uint32_t subscription_id = ++last_subscription_id_;
501   mojo::Remote<blink::mojom::MediaDevicesListener> media_devices_listener;
502   media_devices_listener.Bind(std::move(listener));
503   media_devices_listener.set_disconnect_handler(
504       base::BindOnce(&MediaDevicesManager::UnsubscribeDeviceChangeNotifications,
505                      weak_factory_.GetWeakPtr(), subscription_id));
506   subscriptions_.emplace(
507       subscription_id,
508       SubscriptionRequest(render_process_id, render_frame_id, subscribe_types,
509                           std::move(media_devices_listener)));
510
511   // Fetch the first device_id_salt for this subscriber's frame, to be able to
512   // later detect changes.
513   GetUIThreadTaskRunner({})->PostTask(
514       FROM_HERE,
515       base::BindOnce(
516           get_salt_and_origin_cb_,
517           GlobalRenderFrameHostId(render_process_id, render_frame_id),
518           base::BindPostTaskToCurrentDefault(base::BindOnce(
519               &MediaDevicesManager::SetSubscriptionLastSeenDeviceIdSalt,
520               weak_factory_.GetWeakPtr(), subscription_id))));
521
522   return subscription_id;
523 }
524
525 void MediaDevicesManager::SetSubscriptionLastSeenDeviceIdSalt(
526     uint32_t subscription_id,
527     const MediaDeviceSaltAndOrigin& salt_and_origin) {
528   auto it = subscriptions_.find(subscription_id);
529
530   if (it == subscriptions_.end())
531     return;
532   SubscriptionRequest& request = it->second;
533
534   request.last_seen_device_id_salt_ = salt_and_origin.device_id_salt();
535 }
536
537 void MediaDevicesManager::UnsubscribeDeviceChangeNotifications(
538     uint32_t subscription_id) {
539   DCHECK_CURRENTLY_ON(BrowserThread::IO);
540   subscriptions_.erase(subscription_id);
541 }
542
543 void MediaDevicesManager::SetCachePolicy(MediaDeviceType type,
544                                          CachePolicy policy) {
545   DCHECK_CURRENTLY_ON(BrowserThread::IO);
546   DCHECK(blink::IsValidMediaDeviceType(type));
547   if (cache_policies_[static_cast<size_t>(type)] == policy)
548     return;
549
550   cache_policies_[static_cast<size_t>(type)] = policy;
551   // If the new policy is SYSTEM_MONITOR, issue an enumeration to populate the
552   // cache.
553   if (policy == CachePolicy::SYSTEM_MONITOR) {
554     cache_infos_[static_cast<size_t>(type)].InvalidateCache();
555     DoEnumerateDevices(type);
556   }
557 }
558
559 void MediaDevicesManager::StartMonitoring() {
560   DCHECK_CURRENTLY_ON(BrowserThread::IO);
561   if (monitoring_started_)
562     return;
563
564   if (!base::SystemMonitor::Get())
565     return;
566
567 #if BUILDFLAG(IS_MAC)
568   if (!base::FeatureList::IsEnabled(features::kDeviceMonitorMac))
569     return;
570 #endif
571
572 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
573   if (base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess)) {
574     DCHECK(!audio_service_device_listener_);
575     audio_service_device_listener_ =
576         std::make_unique<AudioServiceDeviceListener>();
577   }
578 #endif
579   SendLogMessage("StartMonitoring()");
580   monitoring_started_ = true;
581   base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
582
583   if (base::FeatureList::IsEnabled(features::kMediaDevicesSystemMonitorCache)) {
584     for (size_t i = 0;
585          i < static_cast<size_t>(MediaDeviceType::kNumMediaDeviceTypes); ++i) {
586       DCHECK(cache_policies_[i] != CachePolicy::SYSTEM_MONITOR);
587       SetCachePolicy(static_cast<MediaDeviceType>(i),
588                      CachePolicy::SYSTEM_MONITOR);
589     }
590   }
591
592 #if BUILDFLAG(IS_MAC)
593   GetUIThreadTaskRunner({})->PostTask(
594       FROM_HERE, base::BindOnce(&MediaDevicesManager::StartMonitoringOnUIThread,
595                                 base::Unretained(this)));
596 #endif
597 }
598
599 #if BUILDFLAG(IS_MAC)
600 void MediaDevicesManager::StartMonitoringOnUIThread() {
601   DCHECK_CURRENTLY_ON(BrowserThread::UI);
602   BrowserMainLoop* browser_main_loop = content::BrowserMainLoop::GetInstance();
603   if (!browser_main_loop)
604     return;
605   browser_main_loop->device_monitor_mac()->StartMonitoring();
606 }
607 #endif
608
609 void MediaDevicesManager::StopMonitoring() {
610   DCHECK_CURRENTLY_ON(BrowserThread::IO);
611   if (!monitoring_started_)
612     return;
613   SendLogMessage(base::StringPrintf("StopMonitoring([this=%p])", this));
614   base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
615   audio_service_device_listener_.reset();
616   monitoring_started_ = false;
617   for (size_t i = 0;
618        i < static_cast<size_t>(MediaDeviceType::kNumMediaDeviceTypes); ++i) {
619     SetCachePolicy(static_cast<MediaDeviceType>(i), CachePolicy::NO_CACHE);
620   }
621 }
622
623 void MediaDevicesManager::OnDevicesChanged(
624     base::SystemMonitor::DeviceType device_type) {
625   DCHECK_CURRENTLY_ON(BrowserThread::IO);
626   switch (device_type) {
627     case base::SystemMonitor::DEVTYPE_AUDIO:
628       HandleDevicesChanged(MediaDeviceType::kMediaAudioInput);
629       HandleDevicesChanged(MediaDeviceType::kMediaAudioOuput);
630       break;
631     case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
632       HandleDevicesChanged(MediaDeviceType::kMediaVideoInput);
633       break;
634     default:
635       break;  // Uninteresting device change.
636   }
637 }
638
639 media::VideoCaptureFormats MediaDevicesManager::GetVideoInputFormats(
640     const std::string& device_id,
641     bool try_in_use_first) {
642   DCHECK_CURRENTLY_ON(BrowserThread::IO);
643   media::VideoCaptureFormats formats;
644
645   if (try_in_use_first) {
646     absl::optional<media::VideoCaptureFormat> format =
647         video_capture_manager_->GetDeviceFormatInUse(
648             blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, device_id);
649     if (format.has_value()) {
650       formats.push_back(format.value());
651       ReplaceInvalidFrameRatesWithFallback(&formats);
652       return formats;
653     }
654   }
655
656   video_capture_manager_->GetDeviceSupportedFormats(device_id, &formats);
657   ReplaceInvalidFrameRatesWithFallback(&formats);
658   // Remove formats that have zero resolution.
659   base::EraseIf(formats, [](const media::VideoCaptureFormat& format) {
660     return format.frame_size.GetArea() <= 0;
661   });
662
663   // If the device does not report any valid format, use a fallback list of
664   // standard formats.
665   if (formats.empty()) {
666     for (const auto& resolution : kFallbackVideoResolutions) {
667       for (const auto frame_rate : kFallbackVideoFrameRates) {
668         formats.push_back(media::VideoCaptureFormat(
669             gfx::Size(resolution.width, resolution.height), frame_rate,
670             media::PIXEL_FORMAT_I420));
671       }
672     }
673   }
674
675   return formats;
676 }
677
678 blink::WebMediaDeviceInfoArray MediaDevicesManager::GetCachedDeviceInfo(
679     MediaDeviceType type) const {
680   DCHECK_CURRENTLY_ON(BrowserThread::IO);
681   return current_snapshot_[static_cast<size_t>(type)];
682 }
683
684 void MediaDevicesManager::RegisterDispatcherHost(
685     std::unique_ptr<blink::mojom::MediaDevicesDispatcherHost> dispatcher_host,
686     mojo::PendingReceiver<blink::mojom::MediaDevicesDispatcherHost> receiver) {
687   DCHECK_CURRENTLY_ON(BrowserThread::IO);
688   dispatcher_hosts_.Add(std::move(dispatcher_host), std::move(receiver));
689 }
690
691 void MediaDevicesManager::SetPermissionChecker(
692     std::unique_ptr<MediaDevicesPermissionChecker> permission_checker) {
693   DCHECK_CURRENTLY_ON(BrowserThread::IO);
694   DCHECK(permission_checker);
695   permission_checker_ = std::move(permission_checker);
696 }
697
698 void MediaDevicesManager::CheckPermissionsForEnumerateDevices(
699     int render_process_id,
700     int render_frame_id,
701     const BoolDeviceTypes& requested_types,
702     bool request_video_input_capabilities,
703     bool request_audio_input_capabilities,
704     EnumerateDevicesCallback callback,
705     const MediaDeviceSaltAndOrigin& salt_and_origin) {
706   DCHECK_CURRENTLY_ON(BrowserThread::IO);
707   permission_checker_->CheckPermissions(
708       requested_types, render_process_id, render_frame_id,
709       base::BindOnce(&MediaDevicesManager::OnPermissionsCheckDone,
710                      weak_factory_.GetWeakPtr(), requested_types,
711                      request_video_input_capabilities,
712                      request_audio_input_capabilities, std::move(callback),
713                      std::move(salt_and_origin)));
714 }
715
716 void MediaDevicesManager::OnPermissionsCheckDone(
717     const MediaDevicesManager::BoolDeviceTypes& requested_types,
718     bool request_video_input_capabilities,
719     bool request_audio_input_capabilities,
720     EnumerateDevicesCallback callback,
721     const MediaDeviceSaltAndOrigin& salt_and_origin,
722     const MediaDevicesManager::BoolDeviceTypes& has_permissions) {
723   DCHECK_CURRENTLY_ON(BrowserThread::IO);
724   // The video-capture subsystem currently does not support group IDs.
725   // If video input devices are requested, also request audio input devices in
726   // order to be able to use an heuristic that guesses group IDs for video
727   // devices by finding matches in audio input devices.
728   // TODO(crbug.com/627793): Remove |internal_requested_types| and use
729   // |requested_types| directly when video capture supports group IDs.
730   BoolDeviceTypes internal_requested_types;
731   internal_requested_types[static_cast<size_t>(
732       MediaDeviceType::kMediaAudioInput)] =
733       requested_types[static_cast<size_t>(MediaDeviceType::kMediaAudioInput)] ||
734       requested_types[static_cast<size_t>(MediaDeviceType::kMediaVideoInput)];
735   internal_requested_types[static_cast<size_t>(
736       MediaDeviceType::kMediaVideoInput)] =
737       requested_types[static_cast<size_t>(MediaDeviceType::kMediaVideoInput)];
738   internal_requested_types[static_cast<size_t>(
739       MediaDeviceType::kMediaAudioOuput)] =
740       requested_types[static_cast<size_t>(MediaDeviceType::kMediaAudioOuput)];
741
742   EnumerateDevices(
743       internal_requested_types,
744       base::BindOnce(&MediaDevicesManager::OnDevicesEnumerated,
745                      weak_factory_.GetWeakPtr(), requested_types,
746                      request_video_input_capabilities,
747                      request_audio_input_capabilities, std::move(callback),
748                      std::move(salt_and_origin), has_permissions));
749 }
750
751 void MediaDevicesManager::OnDevicesEnumerated(
752     const MediaDevicesManager::BoolDeviceTypes& requested_types,
753     bool request_video_input_capabilities,
754     bool request_audio_input_capabilities,
755     EnumerateDevicesCallback callback,
756     const MediaDeviceSaltAndOrigin& salt_and_origin,
757     const MediaDevicesManager::BoolDeviceTypes& has_permissions,
758     const MediaDeviceEnumeration& enumeration) {
759   DCHECK_CURRENTLY_ON(BrowserThread::IO);
760
761   const bool video_input_capabilities_requested =
762       has_permissions[static_cast<size_t>(MediaDeviceType::kMediaVideoInput)] &&
763       request_video_input_capabilities;
764   const bool audio_input_capabilities_requested =
765       has_permissions[static_cast<size_t>(MediaDeviceType::kMediaAudioInput)] &&
766       request_audio_input_capabilities;
767
768   std::vector<blink::WebMediaDeviceInfoArray> translation(
769       static_cast<size_t>(MediaDeviceType::kNumMediaDeviceTypes));
770   for (size_t i = 0;
771        i < static_cast<size_t>(MediaDeviceType::kNumMediaDeviceTypes); ++i) {
772     if (!requested_types[i])
773       continue;
774
775     for (const auto& device_info : enumeration[i]) {
776       if (base::FeatureList::IsEnabled(
777               features::kEnumerateDevicesHideDeviceIDs) &&
778           !has_permissions[i] && !translation[i].empty())
779         break;
780
781       translation[i].push_back(TranslateMediaDeviceInfo(
782           has_permissions[i], salt_and_origin, device_info));
783     }
784   }
785
786   GetAudioInputCapabilities(video_input_capabilities_requested,
787                             audio_input_capabilities_requested,
788                             std::move(callback), enumeration, translation);
789 }
790
791 void MediaDevicesManager::GetAudioInputCapabilities(
792     bool request_video_input_capabilities,
793     bool request_audio_input_capabilities,
794     EnumerateDevicesCallback callback,
795     const MediaDeviceEnumeration& raw_enumeration_results,
796     const std::vector<blink::WebMediaDeviceInfoArray>&
797         hashed_enumeration_results) {
798   DCHECK_CURRENTLY_ON(BrowserThread::IO);
799
800   EnumerationState state;
801   size_t state_id = next_enumeration_state_id_++;
802   state.video_input_capabilities_requested = request_video_input_capabilities;
803   state.audio_input_capabilities_requested = request_audio_input_capabilities;
804   state.completion_cb = std::move(callback);
805   state.raw_enumeration_results = raw_enumeration_results;
806   state.hashed_enumeration_results = hashed_enumeration_results;
807   state.num_pending_audio_input_capabilities =
808       hashed_enumeration_results[static_cast<size_t>(
809                                      MediaDeviceType::kMediaAudioInput)]
810           .size();
811
812   if (!state.audio_input_capabilities_requested ||
813       state.num_pending_audio_input_capabilities == 0) {
814     FinalizeDevicesEnumerated(std::move(state));
815     return;
816   }
817
818   enumeration_states_[state_id] = std::move(state);
819   DCHECK_EQ(raw_enumeration_results[static_cast<size_t>(
820                                         MediaDeviceType::kMediaAudioInput)]
821                 .size(),
822             hashed_enumeration_results[static_cast<size_t>(
823                                            MediaDeviceType::kMediaAudioInput)]
824                 .size());
825   std::size_t num_audio_input_devices =
826       raw_enumeration_results[static_cast<size_t>(
827                                   MediaDeviceType::kMediaAudioInput)]
828           .size();
829   for (std::size_t i = 0; i < num_audio_input_devices; i++) {
830     auto raw_device_info = raw_enumeration_results[static_cast<size_t>(
831         MediaDeviceType::kMediaAudioInput)][i];
832     auto hashed_device_info = hashed_enumeration_results[static_cast<size_t>(
833         MediaDeviceType::kMediaAudioInput)][i];
834
835     AudioInputDeviceCapabilitiesPtr capabilities =
836         blink::mojom::AudioInputDeviceCapabilities::New();
837     capabilities->device_id = hashed_device_info.device_id;
838     capabilities->parameters =
839         media::AudioParameters::UnavailableDeviceParams();
840     enumeration_states_[state_id].audio_capabilities.push_back(
841         std::move(capabilities));
842     size_t capabilities_index =
843         enumeration_states_[state_id].audio_capabilities.size() - 1;
844     if (use_fake_devices_) {
845       GetIOThreadTaskRunner({})->PostTask(
846           FROM_HERE,
847           base::BindOnce(&MediaDevicesManager::GotAudioInputCapabilities,
848                          weak_factory_.GetWeakPtr(), state_id,
849                          capabilities_index,
850                          media::AudioParameters::UnavailableDeviceParams()));
851     } else {
852       audio_system_->GetInputStreamParameters(
853           raw_device_info.device_id,
854           base::BindOnce(&MediaDevicesManager::GotAudioInputCapabilities,
855                          weak_factory_.GetWeakPtr(), state_id,
856                          capabilities_index));
857     }
858   }
859 }
860
861 void MediaDevicesManager::GotAudioInputCapabilities(
862     size_t state_id,
863     size_t capabilities_index,
864     const absl::optional<media::AudioParameters>& parameters) {
865   DCHECK_CURRENTLY_ON(BrowserThread::IO);
866   DCHECK(base::Contains(enumeration_states_, state_id));
867
868   auto& enumeration_state = enumeration_states_[state_id];
869   DCHECK_GT(enumeration_state.num_pending_audio_input_capabilities, 0);
870
871   AudioInputDeviceCapabilitiesPtr& capabilities =
872       enumeration_state.audio_capabilities[capabilities_index];
873   if (parameters) {
874     capabilities->parameters = *parameters;
875     // Data from the |parameters| field is duplicated in the |channels|,
876     // |sample_rate| and |latency| fields due to the lack of availability
877     // of the media::AudioParameters native mojo mapping in blink.
878     // TODO(crbug.com/787252): Remove redundant fields when |parameters|
879     // is accessible from Blink.
880     capabilities->is_valid = parameters->IsValid();
881     capabilities->channels = parameters->channels();
882     capabilities->sample_rate = parameters->sample_rate();
883     capabilities->latency = parameters->GetBufferDuration();
884   }
885   DCHECK(capabilities->parameters.IsValid());
886
887   if (--enumeration_state.num_pending_audio_input_capabilities == 0) {
888     FinalizeDevicesEnumerated(std::move(enumeration_state));
889     enumeration_states_.erase(state_id);
890   }
891 }
892
893 void MediaDevicesManager::FinalizeDevicesEnumerated(
894     EnumerationState enumeration_state) {
895   std::move(enumeration_state.completion_cb)
896       .Run(std::move(enumeration_state.hashed_enumeration_results),
897            enumeration_state.video_input_capabilities_requested
898                ? ComputeVideoInputCapabilities(
899                      enumeration_state
900                          .raw_enumeration_results[static_cast<size_t>(
901                              MediaDeviceType::kMediaVideoInput)],
902                      enumeration_state
903                          .hashed_enumeration_results[static_cast<size_t>(
904                              MediaDeviceType::kMediaVideoInput)])
905                : std::vector<VideoInputDeviceCapabilitiesPtr>(),
906            std::move(enumeration_state.audio_capabilities));
907 }
908
909 std::vector<VideoInputDeviceCapabilitiesPtr>
910 MediaDevicesManager::ComputeVideoInputCapabilities(
911     const blink::WebMediaDeviceInfoArray& raw_device_infos,
912     const blink::WebMediaDeviceInfoArray& translated_device_infos) {
913   DCHECK_EQ(raw_device_infos.size(), translated_device_infos.size());
914   std::vector<VideoInputDeviceCapabilitiesPtr> video_input_capabilities;
915   for (size_t i = 0; i < raw_device_infos.size(); ++i) {
916     VideoInputDeviceCapabilitiesPtr capabilities =
917         blink::mojom::VideoInputDeviceCapabilities::New();
918     capabilities->device_id = translated_device_infos[i].device_id;
919     capabilities->formats = GetVideoInputFormats(raw_device_infos[i].device_id,
920                                                  false /* try_in_use_first */);
921     capabilities->facing_mode = translated_device_infos[i].video_facing;
922     video_input_capabilities.push_back(std::move(capabilities));
923   }
924   return video_input_capabilities;
925 }
926
927 void MediaDevicesManager::DoEnumerateDevices(MediaDeviceType type) {
928   DCHECK_CURRENTLY_ON(BrowserThread::IO);
929   DCHECK(blink::IsValidMediaDeviceType(type));
930   CacheInfo& cache_info = cache_infos_[static_cast<size_t>(type)];
931   if (cache_info.is_update_ongoing())
932     return;
933   SendLogMessage(base::StringPrintf("DoEnumerateDevices({type=%s})",
934                                     DeviceTypeToString(type)));
935
936   cache_info.UpdateStarted();
937   switch (type) {
938     case MediaDeviceType::kMediaAudioInput:
939       EnumerateAudioDevices(true /* is_input */);
940       break;
941     case MediaDeviceType::kMediaVideoInput:
942       ReportVideoEnumerationStart();
943       video_capture_manager_->EnumerateDevices(
944           base::BindOnce(&MediaDevicesManager::VideoInputDevicesEnumerated,
945                          weak_factory_.GetWeakPtr()));
946       break;
947     case MediaDeviceType::kMediaAudioOuput:
948       EnumerateAudioDevices(false /* is_input */);
949       break;
950     default:
951       NOTREACHED();
952   }
953 }
954
955 void MediaDevicesManager::EnumerateAudioDevices(bool is_input) {
956   DCHECK_CURRENTLY_ON(BrowserThread::IO);
957   MediaDeviceType type = is_input ? MediaDeviceType::kMediaAudioInput
958                                   : MediaDeviceType::kMediaAudioOuput;
959   if (use_fake_devices_) {
960     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
961         FROM_HERE, base::BindOnce(&MediaDevicesManager::DevicesEnumerated,
962                                   weak_factory_.GetWeakPtr(), type,
963                                   GetFakeAudioDevices(is_input)));
964     return;
965   }
966
967   audio_system_->GetDeviceDescriptions(
968       is_input, base::BindOnce(&MediaDevicesManager::AudioDevicesEnumerated,
969                                weak_factory_.GetWeakPtr(), type));
970 }
971
972 void MediaDevicesManager::VideoInputDevicesEnumerated(
973     DeviceEnumerationResult result_code,
974     const media::VideoCaptureDeviceDescriptors& descriptors) {
975   DCHECK_CURRENTLY_ON(BrowserThread::IO);
976   ReportVideoEnumerationResult(result_code);
977
978   if (result_code != DeviceEnumerationResult::kSuccess) {
979     std::string log_message =
980         base::StringPrintf("VideoInputDevicesEnumerated got error %d",
981                            static_cast<int>(result_code));
982     // Log to both WebRTC logs (for feedback reports) and text logs for
983     // manually-collected chrome logs at customers.
984     SendLogMessage(log_message);
985     VLOG(1) << log_message;
986     // TODO(crbug.com/1313822): Propagate this as an error response to the
987     // page and expose in the JS API.
988   }
989   blink::WebMediaDeviceInfoArray snapshot;
990   for (const auto& descriptor : descriptors) {
991     snapshot.emplace_back(descriptor);
992   }
993   DevicesEnumerated(MediaDeviceType::kMediaVideoInput, snapshot);
994 }
995
996 void MediaDevicesManager::AudioDevicesEnumerated(
997     MediaDeviceType type,
998     media::AudioDeviceDescriptions device_descriptions) {
999   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1000
1001   blink::WebMediaDeviceInfoArray snapshot;
1002   for (const media::AudioDeviceDescription& description : device_descriptions) {
1003     snapshot.emplace_back(
1004         description.unique_id, description.device_name, description.group_id,
1005         media::VideoCaptureControlSupport(), blink::mojom::FacingMode::kNone);
1006   }
1007   DevicesEnumerated(type, snapshot);
1008 }
1009
1010 void MediaDevicesManager::DevicesEnumerated(
1011     MediaDeviceType type,
1012     const blink::WebMediaDeviceInfoArray& snapshot) {
1013   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1014   DCHECK(blink::IsValidMediaDeviceType(type));
1015   UpdateSnapshot(type, snapshot);
1016   cache_infos_[static_cast<size_t>(type)].UpdateCompleted();
1017   has_seen_result_[static_cast<size_t>(type)] = true;
1018   SendLogMessage(GetDevicesEnumeratedLogString(type, snapshot));
1019
1020   if (cache_policies_[static_cast<size_t>(type)] == CachePolicy::NO_CACHE) {
1021     for (auto& request : requests_)
1022       request.has_seen_result[static_cast<size_t>(type)] = true;
1023   }
1024
1025   // Note that IsLastUpdateValid is always true when policy is NO_CACHE.
1026   if (cache_infos_[static_cast<size_t>(type)].IsLastUpdateValid()) {
1027     ProcessRequests();
1028   } else {
1029     DoEnumerateDevices(type);
1030   }
1031
1032 #if BUILDFLAG(IS_TIZEN_TV)
1033   // report device list to webbrowser
1034   if (IsWebBrowser() && !enum_cb_.is_null()) {
1035     got_result_[static_cast<size_t>(type)] = true;
1036
1037     for (const auto& device : snapshot) {
1038       device_infos_[static_cast<size_t>(type)].emplace_back(
1039           device.device_id, device.label, device.group_id);
1040     }
1041
1042     bool finish = std::all_of(got_result_.begin(), got_result_.end(),
1043                               [](const bool result) { return result == true; });
1044     if (finish)
1045       std::move(enum_cb_).Run(device_infos_);
1046   }
1047 #endif
1048 }
1049
1050 void MediaDevicesManager::UpdateSnapshot(
1051     MediaDeviceType type,
1052     const blink::WebMediaDeviceInfoArray& new_snapshot,
1053     bool ignore_group_id) {
1054   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1055   DCHECK(blink::IsValidMediaDeviceType(type));
1056
1057   bool need_update_device_change_subscribers = false;
1058   blink::WebMediaDeviceInfoArray& old_snapshot =
1059       current_snapshot_[static_cast<size_t>(type)];
1060
1061   if (type == MediaDeviceType::kMediaAudioInput ||
1062       type == MediaDeviceType::kMediaVideoInput) {
1063     MaybeStopRemovedInputDevices(type, new_snapshot);
1064   }
1065
1066   // Update the cached snapshot and send notifications only if the device list
1067   // has changed.
1068   if (!base::ranges::equal(
1069           new_snapshot, old_snapshot,
1070           ignore_group_id
1071               ? [](const blink::WebMediaDeviceInfo& lhs,
1072                    const blink::WebMediaDeviceInfo& rhs) { return lhs == rhs; }
1073               : EqualDeviceAndGroupID)) {
1074     // Prevent sending notifications until group IDs are updated using
1075     // a heuristic in ProcessRequests().
1076     // TODO(crbug.com/627793): Remove |is_video_with_group_ids| and the
1077     // corresponding checks when the video-capture subsystem supports
1078     // group IDs.
1079     bool is_video_with_good_group_ids =
1080         type == MediaDeviceType::kMediaVideoInput &&
1081         (new_snapshot.size() == 0 || !new_snapshot[0].group_id.empty());
1082     if (type == MediaDeviceType::kMediaAudioInput ||
1083         is_video_with_good_group_ids) {
1084       ui_input_device_change_cb_.Run(type, new_snapshot);
1085     }
1086
1087     // Do not notify device-change subscribers after the first enumeration
1088     // result, since it is not due to an actual device change.
1089     need_update_device_change_subscribers =
1090         has_seen_result_[static_cast<size_t>(type)] &&
1091         (old_snapshot.size() != 0 || new_snapshot.size() != 0) &&
1092         (type != MediaDeviceType::kMediaVideoInput ||
1093          is_video_with_good_group_ids);
1094     current_snapshot_[static_cast<size_t>(type)] = new_snapshot;
1095   }
1096
1097   // Generate salts for each subscriber even if the device list hasn't changed,
1098   // as we may need to notify them anyway.
1099   for (const auto& subscription : subscriptions_) {
1100     const SubscriptionRequest& request = subscription.second;
1101     if (request.subscribe_types[static_cast<size_t>(type)]) {
1102       GetUIThreadTaskRunner({})->PostTask(
1103           FROM_HERE,
1104           base::BindOnce(
1105               get_salt_and_origin_cb_,
1106               GlobalRenderFrameHostId(request.render_process_id,
1107                                       request.render_frame_id),
1108               base::BindPostTaskToCurrentDefault(base::BindOnce(
1109                   &MediaDevicesManager::OnSaltAndOriginForSubscription,
1110                   weak_factory_.GetWeakPtr(), subscription.first,
1111                   request.render_process_id, request.render_frame_id, type,
1112                   new_snapshot, need_update_device_change_subscribers))));
1113     }
1114   }
1115 }
1116
1117 void MediaDevicesManager::ProcessRequests() {
1118   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1119   // Populate the group ID field for video devices using a heuristic that looks
1120   // for device coincidences with audio input devices.
1121   // TODO(crbug.com/627793): Remove this once the video-capture subsystem
1122   // supports group IDs.
1123   if (has_seen_result_[static_cast<size_t>(
1124           MediaDeviceType::kMediaVideoInput)]) {
1125     blink::WebMediaDeviceInfoArray video_devices =
1126         current_snapshot_[static_cast<size_t>(
1127             MediaDeviceType::kMediaVideoInput)];
1128     for (auto& video_device_info : video_devices) {
1129       video_device_info.group_id =
1130           GuessVideoGroupID(current_snapshot_[static_cast<size_t>(
1131                                 MediaDeviceType::kMediaAudioInput)],
1132                             video_device_info);
1133     }
1134     UpdateSnapshot(MediaDeviceType::kMediaVideoInput, video_devices,
1135                    false /* ignore_group_id */);
1136   }
1137
1138   base::EraseIf(requests_, [this](EnumerationRequest& request) {
1139     if (IsEnumerationRequestReady(request)) {
1140       std::move(request.callback).Run(current_snapshot_);
1141       return true;
1142     }
1143     return false;
1144   });
1145 }
1146
1147 bool MediaDevicesManager::IsEnumerationRequestReady(
1148     const EnumerationRequest& request_info) {
1149   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1150   bool is_ready = true;
1151   for (size_t i = 0;
1152        i < static_cast<size_t>(MediaDeviceType::kNumMediaDeviceTypes); ++i) {
1153     if (!request_info.requested[i])
1154       continue;
1155     switch (cache_policies_[i]) {
1156       case CachePolicy::SYSTEM_MONITOR:
1157         if (!cache_infos_[i].IsLastUpdateValid())
1158           is_ready = false;
1159         break;
1160       case CachePolicy::NO_CACHE:
1161         if (!request_info.has_seen_result[i])
1162           is_ready = false;
1163         break;
1164       default:
1165         NOTREACHED();
1166     }
1167   }
1168   return is_ready;
1169 }
1170
1171 void MediaDevicesManager::HandleDevicesChanged(MediaDeviceType type) {
1172   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1173   DCHECK(blink::IsValidMediaDeviceType(type));
1174   if (!cache_infos_[static_cast<size_t>(type)].is_update_ongoing()) {
1175     SendLogMessage(base::StringPrintf("HandleDevicesChanged({type=%s}",
1176                                       DeviceTypeToString(type)));
1177   }
1178   cache_infos_[static_cast<size_t>(type)].InvalidateCache();
1179   DoEnumerateDevices(type);
1180 }
1181
1182 void MediaDevicesManager::MaybeStopRemovedInputDevices(
1183     MediaDeviceType type,
1184     const blink::WebMediaDeviceInfoArray& new_snapshot) {
1185   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1186   DCHECK(type == MediaDeviceType::kMediaAudioInput ||
1187          type == MediaDeviceType::kMediaVideoInput);
1188
1189   std::vector<blink::WebMediaDeviceInfo> removed_audio_devices;
1190   for (const auto& old_device_info :
1191        current_snapshot_[static_cast<size_t>(type)]) {
1192     // If a device was removed, notify the MediaStreamManager to stop all
1193     // streams using that device.
1194     if (!base::Contains(new_snapshot, old_device_info.device_id,
1195                         &blink::WebMediaDeviceInfo::device_id)) {
1196       stop_removed_input_device_cb_.Run(type, old_device_info);
1197
1198       if (type == MediaDeviceType::kMediaAudioInput) {
1199         removed_audio_devices.push_back(old_device_info);
1200       }
1201     }
1202   }
1203
1204   // "default" and "communications" audio devices that have been removed,
1205   // require an extra notification. In fact, such audio devices have associated
1206   // virtual audio devices in the snapshot with the special "default" or
1207   // "communications" IDs. The code below implements an heuristic, such that to
1208   // identify if an audio device was default, it checks whether the old
1209   // snapshot contained an audio device with the same group ID and device ID
1210   // matching either "default" or "communications".
1211   // NOTE: ChromeOS is able to seamlessly redirect streams to the new default
1212   // device, hence the event should not be triggered.
1213 #if !BUILDFLAG(IS_CHROMEOS)
1214   for (const auto& removed_audio_device : removed_audio_devices) {
1215     for (const auto& old_device_info :
1216          current_snapshot_[static_cast<size_t>(type)]) {
1217       if (removed_audio_device.group_id == old_device_info.group_id &&
1218           (old_device_info.device_id ==
1219                media::AudioDeviceDescription::kDefaultDeviceId ||
1220            old_device_info.device_id ==
1221                media::AudioDeviceDescription::kCommunicationsDeviceId)) {
1222         stop_removed_input_device_cb_.Run(type, old_device_info);
1223       }
1224     }
1225   }
1226 #endif  // !BUILDFLAG(IS_CHROMEOS)
1227 }
1228
1229 void MediaDevicesManager::OnSaltAndOriginForSubscription(
1230     uint32_t subscription_id,
1231     int render_process_id,
1232     int render_frame_id,
1233     MediaDeviceType type,
1234     const blink::WebMediaDeviceInfoArray& device_infos,
1235     bool devices_changed,
1236     const MediaDeviceSaltAndOrigin& salt_and_origin) {
1237   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1238
1239   auto it = subscriptions_.find(subscription_id);
1240   if (it == subscriptions_.end())
1241     return;
1242   SubscriptionRequest& request = it->second;
1243
1244   // Continue to propagate a change notification if either the actual device
1245   // list has changed, or the device_id_salt has changed.
1246   bool salt_reset =
1247       request.last_seen_device_id_salt_ &&
1248       salt_and_origin.device_id_salt() != request.last_seen_device_id_salt_;
1249
1250   if (devices_changed || salt_reset) {
1251     MediaDevicesManager::CheckPermissionForDeviceChange(
1252         subscription_id, render_process_id, render_frame_id, type, device_infos,
1253         salt_and_origin);
1254   }
1255   request.last_seen_device_id_salt_ = salt_and_origin.device_id_salt();
1256 }
1257
1258 void MediaDevicesManager::CheckPermissionForDeviceChange(
1259     uint32_t subscription_id,
1260     int render_process_id,
1261     int render_frame_id,
1262     MediaDeviceType type,
1263     const blink::WebMediaDeviceInfoArray& device_infos,
1264     const MediaDeviceSaltAndOrigin& salt_and_origin) {
1265   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1266   permission_checker_->CheckPermission(
1267       type, render_process_id, render_frame_id,
1268       base::BindOnce(&MediaDevicesManager::NotifyDeviceChange,
1269                      weak_factory_.GetWeakPtr(), subscription_id, type,
1270                      device_infos, salt_and_origin));
1271 }
1272
1273 void MediaDevicesManager::NotifyDeviceChange(
1274     uint32_t subscription_id,
1275     MediaDeviceType type,
1276     const blink::WebMediaDeviceInfoArray& device_infos,
1277     const MediaDeviceSaltAndOrigin& salt_and_origin,
1278     bool has_permission) {
1279   DCHECK_CURRENTLY_ON(BrowserThread::IO);
1280   DCHECK(blink::IsValidMediaDeviceType(type));
1281   auto it = subscriptions_.find(subscription_id);
1282   if (it == subscriptions_.end())
1283     return;
1284   SendLogMessage(
1285       base::StringPrintf("NotifyDeviceChange({subscription_id=%u}, {type=%s}",
1286                          subscription_id, DeviceTypeToString(type)));
1287
1288   const SubscriptionRequest& request = it->second;
1289   request.listener_->OnDevicesChanged(
1290       type, TranslateMediaDeviceInfoArray(has_permission, salt_and_origin,
1291                                           device_infos));
1292 }
1293
1294 MediaDevicesManager::EnumerationState::EnumerationState() = default;
1295 MediaDevicesManager::EnumerationState::EnumerationState(
1296     EnumerationState&& other) = default;
1297 MediaDevicesManager::EnumerationState::~EnumerationState() = default;
1298 MediaDevicesManager::EnumerationState& MediaDevicesManager::EnumerationState::
1299 operator=(EnumerationState&& other) = default;
1300
1301 }  // namespace content