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.
5 #include "chrome/browser/media/media_stream_devices_controller.h"
7 #include "base/metrics/histogram.h"
8 #include "base/prefs/scoped_user_pref_update.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
12 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
13 #include "chrome/browser/media/media_permission.h"
14 #include "chrome/browser/media/media_stream_capture_indicator.h"
15 #include "chrome/browser/media/media_stream_device_permissions.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "components/content_settings/core/browser/host_content_settings_map.h"
22 #include "components/content_settings/core/common/content_settings_pattern.h"
23 #include "components/pref_registry/pref_registry_syncable.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/render_widget_host_view.h"
26 #include "content/public/common/media_stream_request.h"
27 #include "content/public/common/origin_util.h"
28 #include "extensions/common/constants.h"
29 #include "grit/theme_resources.h"
30 #include "ui/base/l10n/l10n_util.h"
32 #if defined(OS_ANDROID)
33 #include "chrome/browser/android/preferences/pref_service_bridge.h"
34 #include "content/public/browser/android/content_view_core.h"
35 #include "ui/android/window_android.h"
38 using content::BrowserThread
;
42 enum DevicePermissionActions
{
47 kPermissionActionsMax
// Must always be last!
50 // Returns true if the given ContentSettingsType is being requested in
52 bool ContentTypeIsRequested(ContentSettingsType type
,
53 const content::MediaStreamRequest
& request
) {
54 if (request
.request_type
== content::MEDIA_OPEN_DEVICE
)
57 if (type
== CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
)
58 return request
.audio_type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
;
60 if (type
== CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
)
61 return request
.video_type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
;
68 MediaStreamDevicesController::MediaStreamDevicesController(
69 content::WebContents
* web_contents
,
70 const content::MediaStreamRequest
& request
,
71 const content::MediaResponseCallback
& callback
)
72 : web_contents_(web_contents
),
75 if (request_
.request_type
== content::MEDIA_OPEN_DEVICE
) {
76 UMA_HISTOGRAM_BOOLEAN("Pepper.SecureOrigin.MediaStreamRequest",
77 content::IsOriginSecure(request_
.security_origin
));
79 profile_
= Profile::FromBrowserContext(web_contents
->GetBrowserContext());
80 content_settings_
= TabSpecificContentSettings::FromWebContents(web_contents
);
82 content::MediaStreamRequestResult denial_reason
= content::MEDIA_DEVICE_OK
;
83 old_audio_setting_
= GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
,
84 request_
, &denial_reason
);
85 old_video_setting_
= GetContentSetting(
86 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
, request_
, &denial_reason
);
88 // If either setting is ask, we show the infobar.
89 if (old_audio_setting_
== CONTENT_SETTING_ASK
||
90 old_video_setting_
== CONTENT_SETTING_ASK
) {
94 // Otherwise we can run the callback immediately.
95 RunCallback(old_audio_setting_
, old_video_setting_
, denial_reason
);
98 MediaStreamDevicesController::~MediaStreamDevicesController() {
99 if (!callback_
.is_null()) {
100 callback_
.Run(content::MediaStreamDevices(),
101 content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN
,
102 scoped_ptr
<content::MediaStreamUI
>());
107 void MediaStreamDevicesController::RegisterProfilePrefs(
108 user_prefs::PrefRegistrySyncable
* prefs
) {
109 prefs
->RegisterBooleanPref(prefs::kVideoCaptureAllowed
, true);
110 prefs
->RegisterBooleanPref(prefs::kAudioCaptureAllowed
, true);
111 prefs
->RegisterListPref(prefs::kVideoCaptureAllowedUrls
);
112 prefs
->RegisterListPref(prefs::kAudioCaptureAllowedUrls
);
115 bool MediaStreamDevicesController::IsAskingForAudio() const {
116 return old_audio_setting_
== CONTENT_SETTING_ASK
;
119 bool MediaStreamDevicesController::IsAskingForVideo() const {
120 return old_video_setting_
== CONTENT_SETTING_ASK
;
123 const std::string
& MediaStreamDevicesController::GetSecurityOriginSpec() const {
124 return request_
.security_origin
.spec();
127 int MediaStreamDevicesController::GetIconId() const {
128 if (IsAskingForVideo())
129 return IDR_INFOBAR_MEDIA_STREAM_CAMERA
;
131 return IDR_INFOBAR_MEDIA_STREAM_MIC
;
134 base::string16
MediaStreamDevicesController::GetMessageText() const {
135 int message_id
= IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO
;
136 if (!IsAskingForAudio())
137 message_id
= IDS_MEDIA_CAPTURE_VIDEO_ONLY
;
138 else if (!IsAskingForVideo())
139 message_id
= IDS_MEDIA_CAPTURE_AUDIO_ONLY
;
140 return l10n_util::GetStringFUTF16(
141 message_id
, base::UTF8ToUTF16(GetSecurityOriginSpec()));
144 base::string16
MediaStreamDevicesController::GetMessageTextFragment() const {
145 int message_id
= IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_PERMISSION_FRAGMENT
;
146 if (!IsAskingForAudio())
147 message_id
= IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT
;
148 else if (!IsAskingForVideo())
149 message_id
= IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT
;
150 return l10n_util::GetStringUTF16(message_id
);
153 bool MediaStreamDevicesController::HasUserGesture() const {
154 return request_
.user_gesture
;
157 GURL
MediaStreamDevicesController::GetRequestingHostname() const {
158 return request_
.security_origin
;
161 void MediaStreamDevicesController::PermissionGranted() {
162 GURL
origin(GetSecurityOriginSpec());
163 if (content::IsOriginSecure(origin
)) {
164 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
165 kAllowHttps
, kPermissionActionsMax
);
167 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
168 kAllowHttp
, kPermissionActionsMax
);
170 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
,
171 old_audio_setting_
, CONTENT_SETTING_ALLOW
),
172 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
,
173 old_video_setting_
, CONTENT_SETTING_ALLOW
),
174 content::MEDIA_DEVICE_PERMISSION_DENIED
);
177 void MediaStreamDevicesController::PermissionDenied() {
178 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
179 kDeny
, kPermissionActionsMax
);
180 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
,
181 old_audio_setting_
, CONTENT_SETTING_BLOCK
),
182 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
,
183 old_video_setting_
, CONTENT_SETTING_BLOCK
),
184 content::MEDIA_DEVICE_PERMISSION_DENIED
);
187 void MediaStreamDevicesController::Cancelled() {
188 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
189 kCancel
, kPermissionActionsMax
);
190 RunCallback(old_audio_setting_
, old_video_setting_
,
191 content::MEDIA_DEVICE_PERMISSION_DISMISSED
);
194 void MediaStreamDevicesController::RequestFinished() {
198 content::MediaStreamDevices
MediaStreamDevicesController::GetDevices(
199 ContentSetting audio_setting
,
200 ContentSetting video_setting
) {
201 bool audio_allowed
= audio_setting
== CONTENT_SETTING_ALLOW
;
202 bool video_allowed
= video_setting
== CONTENT_SETTING_ALLOW
;
204 if (!audio_allowed
&& !video_allowed
)
205 return content::MediaStreamDevices();
207 content::MediaStreamDevices devices
;
208 switch (request_
.request_type
) {
209 case content::MEDIA_OPEN_DEVICE
: {
210 const content::MediaStreamDevice
* device
= NULL
;
211 // For open device request, when requested device_id is empty, pick
212 // the first available of the given type. If requested device_id is
213 // not empty, return the desired device if it's available. Otherwise,
216 request_
.audio_type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
) {
217 DCHECK_EQ(content::MEDIA_NO_SERVICE
, request_
.video_type
);
218 if (!request_
.requested_audio_device_id
.empty()) {
220 MediaCaptureDevicesDispatcher::GetInstance()
221 ->GetRequestedAudioDevice(request_
.requested_audio_device_id
);
223 device
= MediaCaptureDevicesDispatcher::GetInstance()
224 ->GetFirstAvailableAudioDevice();
226 } else if (video_allowed
&&
227 request_
.video_type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
) {
228 DCHECK_EQ(content::MEDIA_NO_SERVICE
, request_
.audio_type
);
229 // Pepper API opens only one device at a time.
230 if (!request_
.requested_video_device_id
.empty()) {
232 MediaCaptureDevicesDispatcher::GetInstance()
233 ->GetRequestedVideoDevice(request_
.requested_video_device_id
);
235 device
= MediaCaptureDevicesDispatcher::GetInstance()
236 ->GetFirstAvailableVideoDevice();
240 devices
.push_back(*device
);
243 case content::MEDIA_GENERATE_STREAM
: {
244 bool get_default_audio_device
= audio_allowed
;
245 bool get_default_video_device
= video_allowed
;
247 // Get the exact audio or video device if an id is specified.
248 if (audio_allowed
&& !request_
.requested_audio_device_id
.empty()) {
249 const content::MediaStreamDevice
* audio_device
=
250 MediaCaptureDevicesDispatcher::GetInstance()
251 ->GetRequestedAudioDevice(request_
.requested_audio_device_id
);
253 devices
.push_back(*audio_device
);
254 get_default_audio_device
= false;
257 if (video_allowed
&& !request_
.requested_video_device_id
.empty()) {
258 const content::MediaStreamDevice
* video_device
=
259 MediaCaptureDevicesDispatcher::GetInstance()
260 ->GetRequestedVideoDevice(request_
.requested_video_device_id
);
262 devices
.push_back(*video_device
);
263 get_default_video_device
= false;
267 // If either or both audio and video devices were requested but not
268 // specified by id, get the default devices.
269 if (get_default_audio_device
|| get_default_video_device
) {
270 MediaCaptureDevicesDispatcher::GetInstance()
271 ->GetDefaultDevicesForProfile(profile_
, get_default_audio_device
,
272 get_default_video_device
, &devices
);
276 case content::MEDIA_DEVICE_ACCESS
: {
277 // Get the default devices for the request.
278 MediaCaptureDevicesDispatcher::GetInstance()->GetDefaultDevicesForProfile(
279 profile_
, audio_allowed
, video_allowed
, &devices
);
282 case content::MEDIA_ENUMERATE_DEVICES
: {
290 profile_
->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
291 ContentSettingsPattern::FromURLNoWildcard(request_
.security_origin
),
292 ContentSettingsPattern::Wildcard(),
293 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
);
296 profile_
->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
297 ContentSettingsPattern::FromURLNoWildcard(request_
.security_origin
),
298 ContentSettingsPattern::Wildcard(),
299 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
);
305 void MediaStreamDevicesController::RunCallback(
306 ContentSetting audio_setting
,
307 ContentSetting video_setting
,
308 content::MediaStreamRequestResult denial_reason
) {
309 StorePermission(audio_setting
, video_setting
);
310 UpdateTabSpecificContentSettings(audio_setting
, video_setting
);
312 content::MediaStreamDevices devices
=
313 GetDevices(audio_setting
, video_setting
);
315 // If either audio or video are allowed then the callback should report
316 // success, otherwise we report |denial_reason|.
317 content::MediaStreamRequestResult request_result
= content::MEDIA_DEVICE_OK
;
318 if (audio_setting
!= CONTENT_SETTING_ALLOW
&&
319 video_setting
!= CONTENT_SETTING_ALLOW
) {
320 DCHECK_NE(content::MEDIA_DEVICE_OK
, denial_reason
);
321 request_result
= denial_reason
;
322 } else if (devices
.empty()) {
323 // Even if one of the content settings was allowed, if there are no devices
324 // at this point we still report a failure.
325 request_result
= content::MEDIA_DEVICE_NO_HARDWARE
;
328 scoped_ptr
<content::MediaStreamUI
> ui
;
329 if (!devices
.empty()) {
330 ui
= MediaCaptureDevicesDispatcher::GetInstance()
331 ->GetMediaStreamCaptureIndicator()
332 ->RegisterMediaStream(web_contents_
, devices
);
334 content::MediaResponseCallback cb
= callback_
;
336 cb
.Run(devices
, request_result
, ui
.Pass());
339 void MediaStreamDevicesController::StorePermission(
340 ContentSetting new_audio_setting
,
341 ContentSetting new_video_setting
) const {
342 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
343 ContentSettingsPattern primary_pattern
=
344 ContentSettingsPattern::FromURLNoWildcard(request_
.security_origin
);
346 bool is_pepper_request
= request_
.request_type
== content::MEDIA_OPEN_DEVICE
;
348 if (IsAskingForAudio() && new_audio_setting
!= CONTENT_SETTING_ASK
) {
349 if (ShouldPersistContentSetting(new_audio_setting
, request_
.security_origin
,
350 is_pepper_request
)) {
351 profile_
->GetHostContentSettingsMap()->SetContentSetting(
352 primary_pattern
, ContentSettingsPattern::Wildcard(),
353 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
, std::string(),
357 if (IsAskingForVideo() && new_video_setting
!= CONTENT_SETTING_ASK
) {
358 if (ShouldPersistContentSetting(new_video_setting
, request_
.security_origin
,
359 is_pepper_request
)) {
360 profile_
->GetHostContentSettingsMap()->SetContentSetting(
361 primary_pattern
, ContentSettingsPattern::Wildcard(),
362 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
, std::string(),
368 void MediaStreamDevicesController::UpdateTabSpecificContentSettings(
369 ContentSetting audio_setting
,
370 ContentSetting video_setting
) const {
371 if (!content_settings_
)
374 TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state
=
375 TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED
;
376 std::string selected_audio_device
;
377 std::string selected_video_device
;
378 std::string requested_audio_device
= request_
.requested_audio_device_id
;
379 std::string requested_video_device
= request_
.requested_video_device_id
;
381 // TODO(raymes): Why do we use the defaults here for the selected devices?
382 // Shouldn't we just use the devices that were actually selected?
383 PrefService
* prefs
= Profile::FromBrowserContext(
384 web_contents_
->GetBrowserContext())->GetPrefs();
385 if (audio_setting
!= CONTENT_SETTING_DEFAULT
) {
386 selected_audio_device
=
387 requested_audio_device
.empty()
388 ? prefs
->GetString(prefs::kDefaultAudioCaptureDevice
)
389 : requested_audio_device
;
390 microphone_camera_state
|=
391 TabSpecificContentSettings::MICROPHONE_ACCESSED
|
392 (audio_setting
== CONTENT_SETTING_ALLOW
394 : TabSpecificContentSettings::MICROPHONE_BLOCKED
);
397 if (video_setting
!= CONTENT_SETTING_DEFAULT
) {
398 selected_video_device
=
399 requested_video_device
.empty()
400 ? prefs
->GetString(prefs::kDefaultVideoCaptureDevice
)
401 : requested_video_device
;
402 microphone_camera_state
|=
403 TabSpecificContentSettings::CAMERA_ACCESSED
|
404 (video_setting
== CONTENT_SETTING_ALLOW
406 : TabSpecificContentSettings::CAMERA_BLOCKED
);
409 content_settings_
->OnMediaStreamPermissionSet(
410 request_
.security_origin
, microphone_camera_state
, selected_audio_device
,
411 selected_video_device
, requested_audio_device
, requested_video_device
);
414 ContentSetting
MediaStreamDevicesController::GetContentSetting(
415 ContentSettingsType content_type
,
416 const content::MediaStreamRequest
& request
,
417 content::MediaStreamRequestResult
* denial_reason
) const {
418 DCHECK(content_type
== CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
||
419 content_type
== CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
);
421 std::string requested_device_id
;
422 if (content_type
== CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
)
423 requested_device_id
= request
.requested_audio_device_id
;
424 else if (content_type
== CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
)
425 requested_device_id
= request
.requested_video_device_id
;
427 if (!IsUserAcceptAllowed(content_type
)) {
428 *denial_reason
= content::MEDIA_DEVICE_PERMISSION_DENIED
;
429 return CONTENT_SETTING_BLOCK
;
432 if (ContentTypeIsRequested(content_type
, request
)) {
433 MediaPermission
permission(
434 content_type
, request
.request_type
, request
.security_origin
,
435 web_contents_
->GetLastCommittedURL().GetOrigin(), profile_
);
436 return permission
.GetPermissionStatusWithDeviceRequired(requested_device_id
,
439 // Return the default content setting if the device is not requested.
440 return CONTENT_SETTING_DEFAULT
;
443 ContentSetting
MediaStreamDevicesController::GetNewSetting(
444 ContentSettingsType content_type
,
445 ContentSetting old_setting
,
446 ContentSetting user_decision
) const {
447 DCHECK(user_decision
== CONTENT_SETTING_ALLOW
||
448 user_decision
== CONTENT_SETTING_BLOCK
);
449 ContentSetting result
= old_setting
;
450 if (old_setting
== CONTENT_SETTING_ASK
) {
451 if (user_decision
== CONTENT_SETTING_ALLOW
&&
452 IsUserAcceptAllowed(content_type
)) {
453 result
= CONTENT_SETTING_ALLOW
;
454 } else if (user_decision
== CONTENT_SETTING_BLOCK
) {
455 result
= CONTENT_SETTING_BLOCK
;
461 bool MediaStreamDevicesController::IsUserAcceptAllowed(
462 ContentSettingsType content_type
) const {
463 #if defined(OS_ANDROID)
464 content::ContentViewCore
* cvc
=
465 content::ContentViewCore::FromWebContents(web_contents_
);
469 ui::WindowAndroid
* window_android
= cvc
->GetWindowAndroid();
473 std::string android_permission
=
474 PrefServiceBridge::GetAndroidPermissionForContentSetting(content_type
);
475 bool android_permission_blocked
= false;
476 if (!android_permission
.empty()) {
477 android_permission_blocked
=
478 !window_android
->HasPermission(android_permission
) &&
479 !window_android
->CanRequestPermission(android_permission
);
481 if (android_permission_blocked
)
484 // Don't approve device requests if the tab was hidden.
485 // TODO(qinmin): Add a test for this. http://crbug.com/396869.
486 // TODO(raymes): Shouldn't this apply to all permissions not just audio/video?
487 return web_contents_
->GetRenderWidgetHostView()->IsShowing();