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 6 #include "content/browser/renderer_host/media/device_request_message_filter.h" 7 8 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_util.h" 10 #include "content/browser/browser_main_loop.h" 11 #include "content/browser/renderer_host/media/media_stream_manager.h" 12 #include "content/common/media/media_stream_messages.h" 13 #include "content/public/browser/resource_context.h" 14 #include "crypto/hmac.h" 15 16 // Clears the MediaStreamDevice.name from all devices in |device_list|. 17 static void ClearDeviceLabels(content::StreamDeviceInfoArray* devices) { 18 for (content::StreamDeviceInfoArray::iterator device_itr = devices->begin(); 19 device_itr != devices->end(); 20 ++device_itr) { 21 device_itr->device.name.clear(); 22 } 23 } 24 25 namespace content { 26 27 DeviceRequestMessageFilter::DeviceRequestMessageFilter( 28 ResourceContext* resource_context, 29 MediaStreamManager* media_stream_manager) 30 : resource_context_(resource_context), 31 media_stream_manager_(media_stream_manager) { 32 DCHECK(resource_context); 33 DCHECK(media_stream_manager); 34 } 35 36 DeviceRequestMessageFilter::~DeviceRequestMessageFilter() { 37 DCHECK(requests_.empty()); 38 } 39 40 struct DeviceRequestMessageFilter::DeviceRequest { 41 DeviceRequest(int request_id, 42 const GURL& origin, 43 const std::string& audio_devices_label, 44 const std::string& video_devices_label) 45 : request_id(request_id), 46 origin(origin), 47 has_audio_returned(false), 48 has_video_returned(false), 49 audio_devices_label(audio_devices_label), 50 video_devices_label(video_devices_label) {} 51 52 int request_id; 53 GURL origin; 54 bool has_audio_returned; 55 bool has_video_returned; 56 std::string audio_devices_label; 57 std::string video_devices_label; 58 StreamDeviceInfoArray audio_devices; 59 StreamDeviceInfoArray video_devices; 60 }; 61 62 void DeviceRequestMessageFilter::StreamGenerated( 63 const std::string& label, 64 const StreamDeviceInfoArray& audio_devices, 65 const StreamDeviceInfoArray& video_devices) { 66 NOTIMPLEMENTED(); 67 } 68 69 void DeviceRequestMessageFilter::StreamGenerationFailed( 70 const std::string& label) { 71 NOTIMPLEMENTED(); 72 } 73 74 void DeviceRequestMessageFilter::StopGeneratedStream( 75 const std::string& label) { 76 NOTIMPLEMENTED(); 77 } 78 79 void DeviceRequestMessageFilter::DevicesEnumerated( 80 const std::string& label, 81 const StreamDeviceInfoArray& new_devices) { 82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 83 84 // Look up the DeviceRequest by id. 85 DeviceRequestList::iterator request_it = requests_.begin(); 86 for (; request_it != requests_.end(); ++request_it) { 87 if (label == request_it->audio_devices_label || 88 label == request_it->video_devices_label) { 89 break; 90 } 91 } 92 DCHECK(request_it != requests_.end()); 93 94 StreamDeviceInfoArray* audio_devices = &request_it->audio_devices; 95 StreamDeviceInfoArray* video_devices = &request_it->video_devices; 96 97 // Store hmac'd device ids instead of raw device ids. 98 if (label == request_it->audio_devices_label) { 99 request_it->has_audio_returned = true; 100 DCHECK(audio_devices->empty()); 101 HmacDeviceIds(request_it->origin, new_devices, audio_devices); 102 } else { 103 DCHECK(label == request_it->video_devices_label); 104 request_it->has_video_returned = true; 105 DCHECK(video_devices->empty()); 106 HmacDeviceIds(request_it->origin, new_devices, video_devices); 107 } 108 109 if (!request_it->has_audio_returned || !request_it->has_video_returned) { 110 // Wait for the rest of the devices to complete. 111 return; 112 } 113 114 // Query for mic and camera permissions. 115 if (!resource_context_->AllowMicAccess(request_it->origin)) 116 ClearDeviceLabels(audio_devices); 117 if (!resource_context_->AllowCameraAccess(request_it->origin)) 118 ClearDeviceLabels(video_devices); 119 120 // Both audio and video devices are ready for copying. 121 StreamDeviceInfoArray all_devices = *audio_devices; 122 all_devices.insert( 123 all_devices.end(), video_devices->begin(), video_devices->end()); 124 125 Send(new MediaStreamMsg_GetSourcesACK(request_it->request_id, all_devices)); 126 127 // TODO(vrk): Rename StopGeneratedStream() to CancelDeviceRequest(). 128 media_stream_manager_->StopGeneratedStream(request_it->audio_devices_label); 129 media_stream_manager_->StopGeneratedStream(request_it->video_devices_label); 130 requests_.erase(request_it); 131 } 132 133 void DeviceRequestMessageFilter::DeviceOpened( 134 const std::string& label, 135 const StreamDeviceInfo& video_device) { 136 NOTIMPLEMENTED(); 137 } 138 139 bool DeviceRequestMessageFilter::OnMessageReceived(const IPC::Message& message, 140 bool* message_was_ok) { 141 bool handled = true; 142 IPC_BEGIN_MESSAGE_MAP_EX(DeviceRequestMessageFilter, message, *message_was_ok) 143 IPC_MESSAGE_HANDLER(MediaStreamHostMsg_GetSources, OnGetSources) 144 IPC_MESSAGE_UNHANDLED(handled = false) 145 IPC_END_MESSAGE_MAP_EX() 146 return handled; 147 } 148 149 void DeviceRequestMessageFilter::OnChannelClosing() { 150 BrowserMessageFilter::OnChannelClosing(); 151 152 // Since the IPC channel is gone, cancel outstanding device requests. 153 for (DeviceRequestList::iterator it = requests_.begin(); 154 it != requests_.end(); 155 ++it) { 156 // TODO(vrk): Rename StopGeneratedStream() to CancelDeviceRequest(). 157 media_stream_manager_->StopGeneratedStream(it->audio_devices_label); 158 media_stream_manager_->StopGeneratedStream(it->video_devices_label); 159 } 160 requests_.clear(); 161 } 162 163 void DeviceRequestMessageFilter::HmacDeviceIds( 164 const GURL& origin, 165 const StreamDeviceInfoArray& raw_devices, 166 StreamDeviceInfoArray* devices_with_guids) { 167 DCHECK(devices_with_guids); 168 169 // Replace raw ids with hmac'd ids before returning to renderer process. 170 for (StreamDeviceInfoArray::const_iterator device_itr = raw_devices.begin(); 171 device_itr != raw_devices.end(); 172 ++device_itr) { 173 crypto::HMAC hmac(crypto::HMAC::SHA256); 174 const size_t digest_length = hmac.DigestLength(); 175 std::vector<uint8> digest(digest_length); 176 bool result = hmac.Init(origin.spec()) && 177 hmac.Sign(device_itr->device.id, &digest[0], digest.size()); 178 DCHECK(result); 179 if (result) { 180 StreamDeviceInfo current_device_info = *device_itr; 181 current_device_info.device.id = 182 StringToLowerASCII(base::HexEncode(&digest[0], digest.size())); 183 devices_with_guids->push_back(current_device_info); 184 } 185 } 186 } 187 188 bool DeviceRequestMessageFilter::DoesRawIdMatchGuid( 189 const GURL& security_origin, 190 const std::string& device_guid, 191 const std::string& raw_device_id) { 192 crypto::HMAC hmac(crypto::HMAC::SHA256); 193 bool result = hmac.Init(security_origin.spec()); 194 DCHECK(result); 195 std::vector<uint8> converted_guid; 196 base::HexStringToBytes(device_guid, &converted_guid); 197 return hmac.Verify( 198 raw_device_id, 199 base::StringPiece(reinterpret_cast<const char*>(&converted_guid[0]), 200 converted_guid.size())); 201 } 202 203 void DeviceRequestMessageFilter::OnGetSources(int request_id, 204 const GURL& security_origin) { 205 // Make request to get audio devices. 206 const std::string& audio_label = media_stream_manager_->EnumerateDevices( 207 this, -1, -1, -1, MEDIA_DEVICE_AUDIO_CAPTURE, security_origin); 208 DCHECK(!audio_label.empty()); 209 210 // Make request for video devices. 211 const std::string& video_label = media_stream_manager_->EnumerateDevices( 212 this, -1, -1, -1, MEDIA_DEVICE_VIDEO_CAPTURE, security_origin); 213 DCHECK(!video_label.empty()); 214 215 requests_.push_back(DeviceRequest( 216 request_id, security_origin, audio_label, video_label)); 217 } 218 219 } // namespace content 220