diff --git a/content/browser/renderer_host/media/audio_input_device_manager.cc b/content/browser/renderer_host/media/audio_input_device_manager.cc index 2723decb20666..968bcff76ab3e 100644 --- a/content/browser/renderer_host/media/audio_input_device_manager.cc +++ b/content/browser/renderer_host/media/audio_input_device_manager.cc @@ -143,8 +143,13 @@ void AudioInputDeviceManager::Close(const base::UnguessableToken& session_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); SendAudioLogMessage("Close({session_id=" + session_id.ToString() + "})"); auto device = GetDevice(session_id); - if (device == devices_.end()) + if (device == devices_.end()) { + // The asynchronous open hasn't completed yet. Record the cancellation so + // that OpenedOnIOThread drops the device instead of leaving an orphaned + // session that no DeviceRequest tracks. + cancelled_pending_sessions_.insert(session_id); return; + } const blink::mojom::MediaStreamType stream_type = device->type; devices_.erase(device); @@ -165,6 +170,11 @@ void AudioInputDeviceManager::OpenedOnIOThread( DCHECK(!input_params || input_params->IsValid()); DCHECK(!matched_output_device_id || !matched_output_device_id->empty()); + if (cancelled_pending_sessions_.erase(session_id)) { + // Close() raced ahead of this callback; do not register the device. + return; + } + SendAudioLogMessage("Opened({session_id=" + session_id.ToString() + "})"); blink::MediaStreamDevice media_stream_device(device.type, device.id, device.name); diff --git a/content/browser/renderer_host/media/audio_input_device_manager.h b/content/browser/renderer_host/media/audio_input_device_manager.h index a632e8bdc96af..1a1a7435037db 100644 --- a/content/browser/renderer_host/media/audio_input_device_manager.h +++ b/content/browser/renderer_host/media/audio_input_device_manager.h @@ -16,6 +16,7 @@ #include #include +#include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "base/observer_list.h" #include "base/threading/thread.h" @@ -73,6 +74,10 @@ class CONTENT_EXPORT AudioInputDeviceManager : public MediaStreamProvider { // Only accessed on Browser::IO thread. base::ObserverList::Unchecked listeners_; blink::MediaStreamDevices devices_; + // Sessions for which Close() was called before the asynchronous Open() + // callback fired. OpenedOnIOThread() must drop these instead of adding them + // to `devices_`. + base::flat_set cancelled_pending_sessions_; const raw_ptr audio_system_; };