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/media_stream_devices_controller.h"
6
7#include "base/command_line.h"
8#include "base/prefs/pref_service.h"
9#include "base/prefs/scoped_user_pref_update.h"
10#include "base/values.h"
11#include "chrome/browser/content_settings/content_settings_provider.h"
12#include "chrome/browser/content_settings/host_content_settings_map.h"
13#include "chrome/browser/content_settings/tab_specific_content_settings.h"
14#include "chrome/browser/media/media_capture_devices_dispatcher.h"
15#include "chrome/browser/media/media_stream_capture_indicator.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/content_settings.h"
20#include "chrome/common/content_settings_pattern.h"
21#include "chrome/common/pref_names.h"
22#include "components/user_prefs/pref_registry_syncable.h"
23#include "content/public/browser/browser_thread.h"
24#include "content/public/common/media_stream_request.h"
25#include "extensions/common/constants.h"
26
27#if defined(OS_CHROMEOS)
28#include "chrome/browser/chromeos/login/user_manager.h"
29#endif
30
31using content::BrowserThread;
32
33namespace {
34
35bool HasAnyAvailableDevice() {
36  const content::MediaStreamDevices& audio_devices =
37      MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices();
38  const content::MediaStreamDevices& video_devices =
39      MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices();
40
41  return !audio_devices.empty() || !video_devices.empty();
42}
43
44bool IsInKioskMode() {
45  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
46    return true;
47
48#if defined(OS_CHROMEOS)
49  const chromeos::UserManager* user_manager = chromeos::UserManager::Get();
50  return user_manager && user_manager->IsLoggedInAsKioskApp();
51#else
52  return false;
53#endif
54}
55
56}  // namespace
57
58MediaStreamDevicesController::MediaStreamTypeSettings::MediaStreamTypeSettings(
59    Permission permission, const std::string& requested_device_id):
60    permission(permission), requested_device_id(requested_device_id) {}
61
62MediaStreamDevicesController::MediaStreamTypeSettings::
63    MediaStreamTypeSettings(): permission(MEDIA_NONE) {}
64
65MediaStreamDevicesController::MediaStreamTypeSettings::
66    ~MediaStreamTypeSettings() {}
67
68MediaStreamDevicesController::MediaStreamDevicesController(
69    content::WebContents* web_contents,
70    const content::MediaStreamRequest& request,
71    const content::MediaResponseCallback& callback)
72    : web_contents_(web_contents),
73      request_(request),
74      callback_(callback) {
75  profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
76  content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents);
77
78  // For MEDIA_OPEN_DEVICE requests (Pepper) we always request both webcam
79  // and microphone to avoid popping two infobars.
80  // We start with setting the requested media type to allowed or blocked
81  // depending on the policy. If not blocked by policy it may be blocked later
82  // in the two remaining filtering steps (by user setting or by user when
83  // clicking the infobar).
84  // TODO(grunell): It's not the nicest solution to let the MEDIA_OPEN_DEVICE
85  // case take a ride on the MEDIA_DEVICE_*_CAPTURE permission. Should be fixed.
86  if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
87      request.request_type == content::MEDIA_OPEN_DEVICE) {
88    if (GetDevicePolicy(prefs::kAudioCaptureAllowed,
89                        prefs::kAudioCaptureAllowedUrls) == ALWAYS_DENY) {
90      request_permissions_.insert(std::make_pair(
91          content::MEDIA_DEVICE_AUDIO_CAPTURE,
92          MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY,
93                                  request.requested_audio_device_id)));
94    } else {
95      request_permissions_.insert(std::make_pair(
96          content::MEDIA_DEVICE_AUDIO_CAPTURE,
97          MediaStreamTypeSettings(MEDIA_ALLOWED,
98                                  request.requested_audio_device_id)));
99    }
100  }
101  if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE ||
102      request.request_type == content::MEDIA_OPEN_DEVICE) {
103    if (GetDevicePolicy(prefs::kVideoCaptureAllowed,
104                        prefs::kVideoCaptureAllowedUrls) == ALWAYS_DENY) {
105      request_permissions_.insert(std::make_pair(
106          content::MEDIA_DEVICE_VIDEO_CAPTURE,
107          MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY,
108                                  request.requested_video_device_id)));
109    } else {
110      request_permissions_.insert(std::make_pair(
111          content::MEDIA_DEVICE_VIDEO_CAPTURE,
112          MediaStreamTypeSettings(MEDIA_ALLOWED,
113                                  request.requested_video_device_id)));
114    }
115  }
116}
117
118MediaStreamDevicesController::~MediaStreamDevicesController() {
119  if (!callback_.is_null()) {
120    callback_.Run(content::MediaStreamDevices(),
121                  scoped_ptr<content::MediaStreamUI>());
122  }
123}
124
125// static
126void MediaStreamDevicesController::RegisterProfilePrefs(
127    user_prefs::PrefRegistrySyncable* prefs) {
128  prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed,
129                             true,
130                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
131  prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed,
132                             true,
133                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
134  prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls,
135                          user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
136  prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls,
137                          user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
138}
139
140
141bool MediaStreamDevicesController::DismissInfoBarAndTakeActionOnSettings() {
142  // Tab capture is allowed for extensions only and infobar is not shown for
143  // extensions.
144  if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE ||
145      request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) {
146    Deny(false);
147    return true;
148  }
149
150  // Deny the request if the security origin is empty, this happens with
151  // file access without |--allow-file-access-from-files| flag.
152  if (request_.security_origin.is_empty()) {
153    Deny(false);
154    return true;
155  }
156
157  // Deny the request if there is no device attached to the OS.
158  if (!HasAnyAvailableDevice()) {
159    Deny(false);
160    return true;
161  }
162
163  // Check if any allow exception has been made for this request.
164  if (IsRequestAllowedByDefault()) {
165    Accept(false);
166    return true;
167  }
168
169  // Filter any parts of the request that have been blocked by default and deny
170  // it if nothing is left to accept.
171  if (FilterBlockedByDefaultDevices() == 0) {
172    Deny(false);
173    return true;
174  }
175
176  // Check if the media default setting is set to block.
177  if (IsDefaultMediaAccessBlocked()) {
178    Deny(false);
179    return true;
180  }
181
182  if (request_.request_type == content::MEDIA_OPEN_DEVICE) {
183    bool no_matched_audio_device =
184        (request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
185         !request_.requested_audio_device_id.empty() &&
186         MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedAudioDevice(
187             request_.requested_audio_device_id) == NULL);
188    bool no_matched_video_device =
189        (request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
190         !request_.requested_video_device_id.empty() &&
191         MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedVideoDevice(
192             request_.requested_video_device_id) == NULL);
193    if (no_matched_audio_device || no_matched_video_device) {
194      Deny(false);
195      return true;
196    }
197  }
198
199  // Show the infobar.
200  return false;
201}
202
203bool MediaStreamDevicesController::HasAudio() const {
204  return IsDeviceAudioCaptureRequestedAndAllowed();
205}
206
207bool MediaStreamDevicesController::HasVideo() const {
208  return IsDeviceVideoCaptureRequestedAndAllowed();
209}
210
211const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
212  return request_.security_origin.spec();
213}
214
215void MediaStreamDevicesController::Accept(bool update_content_setting) {
216  NotifyUIRequestAccepted();
217
218  // Get the default devices for the request.
219  content::MediaStreamDevices devices;
220  bool audio_allowed = IsDeviceAudioCaptureRequestedAndAllowed();
221  bool video_allowed = IsDeviceVideoCaptureRequestedAndAllowed();
222  if (audio_allowed || video_allowed) {
223    switch (request_.request_type) {
224      case content::MEDIA_OPEN_DEVICE: {
225        const content::MediaStreamDevice* device = NULL;
226        // For open device request, when requested device_id is empty, pick
227        // the first available of the given type. If requested device_id is
228        // not empty, return the desired device if it's available. Otherwise,
229        // return no device.
230        if (audio_allowed &&
231            request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
232          if (!request_.requested_audio_device_id.empty()) {
233            device = MediaCaptureDevicesDispatcher::GetInstance()->
234                GetRequestedAudioDevice(request_.requested_audio_device_id);
235          } else {
236            device = MediaCaptureDevicesDispatcher::GetInstance()->
237                GetFirstAvailableAudioDevice();
238          }
239        } else if (video_allowed &&
240            request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
241          // Pepper API opens only one device at a time.
242          if (!request_.requested_video_device_id.empty()) {
243            device = MediaCaptureDevicesDispatcher::GetInstance()->
244                GetRequestedVideoDevice(request_.requested_video_device_id);
245          } else {
246            device = MediaCaptureDevicesDispatcher::GetInstance()->
247                GetFirstAvailableVideoDevice();
248          }
249        }
250        if (device)
251          devices.push_back(*device);
252        break;
253      }
254      case content::MEDIA_GENERATE_STREAM: {
255        bool get_default_audio_device = audio_allowed;
256        bool get_default_video_device = video_allowed;
257
258        // Get the exact audio or video device if an id is specified.
259        if (audio_allowed && !request_.requested_audio_device_id.empty()) {
260          const content::MediaStreamDevice* audio_device =
261              MediaCaptureDevicesDispatcher::GetInstance()->
262                  GetRequestedAudioDevice(request_.requested_audio_device_id);
263          if (audio_device) {
264            devices.push_back(*audio_device);
265            get_default_audio_device = false;
266          }
267        }
268        if (video_allowed && !request_.requested_video_device_id.empty()) {
269          const content::MediaStreamDevice* video_device =
270              MediaCaptureDevicesDispatcher::GetInstance()->
271                  GetRequestedVideoDevice(request_.requested_video_device_id);
272          if (video_device) {
273            devices.push_back(*video_device);
274            get_default_video_device = false;
275          }
276        }
277
278        // If either or both audio and video devices were requested but not
279        // specified by id, get the default devices.
280        if (get_default_audio_device || get_default_video_device) {
281          MediaCaptureDevicesDispatcher::GetInstance()->
282              GetDefaultDevicesForProfile(profile_,
283                                          get_default_audio_device,
284                                          get_default_video_device,
285                                          &devices);
286        }
287        break;
288      }
289      case content::MEDIA_DEVICE_ACCESS: {
290        // Get the default devices for the request.
291        MediaCaptureDevicesDispatcher::GetInstance()->
292            GetDefaultDevicesForProfile(profile_,
293                                        audio_allowed,
294                                        video_allowed,
295                                        &devices);
296        break;
297      }
298      case content::MEDIA_ENUMERATE_DEVICES: {
299        // Do nothing.
300        NOTREACHED();
301        break;
302      }
303    }  // switch
304
305    // TODO(raymes): We currently set the content permission for non-https
306    // websites for Pepper requests as well. This is temporary and should be
307    // removed.
308    if (update_content_setting) {
309      if ((IsSchemeSecure() && !devices.empty()) ||
310          request_.request_type == content::MEDIA_OPEN_DEVICE) {
311        SetPermission(true);
312      }
313    }
314  }
315
316  scoped_ptr<content::MediaStreamUI> ui;
317  if (!devices.empty()) {
318    ui = MediaCaptureDevicesDispatcher::GetInstance()->
319        GetMediaStreamCaptureIndicator()->RegisterMediaStream(
320            web_contents_, devices);
321  }
322  content::MediaResponseCallback cb = callback_;
323  callback_.Reset();
324  cb.Run(devices, ui.Pass());
325}
326
327void MediaStreamDevicesController::Deny(bool update_content_setting) {
328  NotifyUIRequestDenied();
329
330  if (update_content_setting)
331    SetPermission(false);
332
333  content::MediaResponseCallback cb = callback_;
334  callback_.Reset();
335  cb.Run(content::MediaStreamDevices(), scoped_ptr<content::MediaStreamUI>());
336}
337
338MediaStreamDevicesController::DevicePolicy
339MediaStreamDevicesController::GetDevicePolicy(
340    const char* policy_name,
341    const char* whitelist_policy_name) const {
342  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343
344  // If the security origin policy matches a value in the whitelist, allow it.
345  // Otherwise, check the |policy_name| master switch for the default behavior.
346
347  PrefService* prefs = profile_->GetPrefs();
348
349  // TODO(tommi): Remove the kiosk mode check when the whitelist below
350  // is visible in the media exceptions UI.
351  // See discussion here: https://codereview.chromium.org/15738004/
352  if (IsInKioskMode()) {
353    const base::ListValue* list = prefs->GetList(whitelist_policy_name);
354    std::string value;
355    for (size_t i = 0; i < list->GetSize(); ++i) {
356      if (list->GetString(i, &value)) {
357        ContentSettingsPattern pattern =
358            ContentSettingsPattern::FromString(value);
359        if (pattern == ContentSettingsPattern::Wildcard()) {
360          DLOG(WARNING) << "Ignoring wildcard URL pattern: " << value;
361          continue;
362        }
363        DLOG_IF(ERROR, !pattern.IsValid()) << "Invalid URL pattern: " << value;
364        if (pattern.IsValid() && pattern.Matches(request_.security_origin))
365          return ALWAYS_ALLOW;
366      }
367    }
368  }
369
370  // If a match was not found, check if audio capture is otherwise disallowed
371  // or if the user should be prompted.  Setting the policy value to "true"
372  // is equal to not setting it at all, so from hereon out, we will return
373  // either POLICY_NOT_SET (prompt) or ALWAYS_DENY (no prompt, no access).
374  if (!prefs->GetBoolean(policy_name))
375    return ALWAYS_DENY;
376
377  return POLICY_NOT_SET;
378}
379
380bool MediaStreamDevicesController::IsRequestAllowedByDefault() const {
381  // The request from internal objects like chrome://URLs is always allowed.
382  if (ShouldAlwaysAllowOrigin())
383    return true;
384
385  struct {
386    bool has_capability;
387    const char* policy_name;
388    const char* list_policy_name;
389    ContentSettingsType settings_type;
390  } device_checks[] = {
391    { IsDeviceAudioCaptureRequestedAndAllowed(), prefs::kAudioCaptureAllowed,
392      prefs::kAudioCaptureAllowedUrls, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC },
393    { IsDeviceVideoCaptureRequestedAndAllowed(), prefs::kVideoCaptureAllowed,
394      prefs::kVideoCaptureAllowedUrls,
395      CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA },
396  };
397
398  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(device_checks); ++i) {
399    if (!device_checks[i].has_capability)
400      continue;
401
402    DevicePolicy policy = GetDevicePolicy(device_checks[i].policy_name,
403                                          device_checks[i].list_policy_name);
404
405    if (policy == ALWAYS_DENY)
406      return false;
407
408    if (policy == POLICY_NOT_SET) {
409      // Only load content settings from secure origins unless it is a
410      // content::MEDIA_OPEN_DEVICE (Pepper) request.
411      if (!IsSchemeSecure() &&
412          request_.request_type != content::MEDIA_OPEN_DEVICE) {
413        return false;
414      }
415      if (profile_->GetHostContentSettingsMap()->GetContentSetting(
416              request_.security_origin, request_.security_origin,
417              device_checks[i].settings_type, NO_RESOURCE_IDENTIFIER) !=
418              CONTENT_SETTING_ALLOW) {
419        return false;
420      }
421    }
422    // If we get here, then either policy is set to ALWAYS_ALLOW or the content
423    // settings allow the request by default.
424  }
425
426  return true;
427}
428
429int MediaStreamDevicesController::FilterBlockedByDefaultDevices() {
430  int requested_devices = 0;
431
432  if (IsDeviceAudioCaptureRequestedAndAllowed()) {
433    if (profile_->GetHostContentSettingsMap()->GetContentSetting(
434        request_.security_origin,
435        request_.security_origin,
436        CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
437        NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_BLOCK) {
438      request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission =
439          MEDIA_BLOCKED_BY_USER_SETTING;
440    } else {
441      ++requested_devices;
442    }
443  }
444
445  if (IsDeviceVideoCaptureRequestedAndAllowed()) {
446    if (profile_->GetHostContentSettingsMap()->GetContentSetting(
447        request_.security_origin,
448        request_.security_origin,
449        CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
450        NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_BLOCK) {
451      request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission =
452          MEDIA_BLOCKED_BY_USER_SETTING;
453    } else {
454      ++requested_devices;
455    }
456  }
457
458  return requested_devices;
459}
460
461bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const {
462  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
463  // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
464  // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
465  // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
466  ContentSetting current_setting =
467      profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
468          CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL);
469  return (current_setting == CONTENT_SETTING_BLOCK);
470}
471
472bool MediaStreamDevicesController::IsSchemeSecure() const {
473  return request_.security_origin.SchemeIsSecure() ||
474      request_.security_origin.SchemeIs(extensions::kExtensionScheme) ||
475      CommandLine::ForCurrentProcess()->HasSwitch(
476          switches::kDisableUserMediaSecurity);
477}
478
479bool MediaStreamDevicesController::ShouldAlwaysAllowOrigin() const {
480  // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
481  // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
482  // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
483  return profile_->GetHostContentSettingsMap()->ShouldAllowAllContent(
484      request_.security_origin, request_.security_origin,
485      CONTENT_SETTINGS_TYPE_MEDIASTREAM);
486}
487
488void MediaStreamDevicesController::SetPermission(bool allowed) const {
489  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
490  ContentSettingsPattern primary_pattern =
491      ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
492  // Check the pattern is valid or not. When the request is from a file access,
493  // no exception will be made.
494  if (!primary_pattern.IsValid())
495    return;
496
497  ContentSetting content_setting = allowed ?
498      CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
499  if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) !=
500      request_permissions_.end()) {
501      profile_->GetHostContentSettingsMap()->SetContentSetting(
502        primary_pattern,
503        ContentSettingsPattern::Wildcard(),
504        CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
505        std::string(),
506        content_setting);
507  }
508  if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) !=
509      request_permissions_.end()) {
510    profile_->GetHostContentSettingsMap()->SetContentSetting(
511        primary_pattern,
512        ContentSettingsPattern::Wildcard(),
513        CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
514        std::string(),
515        content_setting);
516  }
517}
518
519void MediaStreamDevicesController::NotifyUIRequestAccepted() const {
520  if (!content_settings_)
521    return;
522
523  content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
524                                                request_permissions_);
525}
526
527void MediaStreamDevicesController::NotifyUIRequestDenied() {
528  if (!content_settings_)
529    return;
530
531  if (IsDeviceAudioCaptureRequestedAndAllowed()) {
532    request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission =
533        MEDIA_BLOCKED_BY_USER;
534  }
535  if (IsDeviceVideoCaptureRequestedAndAllowed()) {
536    request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission =
537        MEDIA_BLOCKED_BY_USER;
538  }
539
540  content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
541                                                request_permissions_);
542}
543
544bool MediaStreamDevicesController::IsDeviceAudioCaptureRequestedAndAllowed()
545    const {
546  MediaStreamTypeSettingsMap::const_iterator it =
547      request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE);
548  return (it != request_permissions_.end() &&
549          it->second.permission == MEDIA_ALLOWED);
550}
551
552bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed()
553    const {
554  MediaStreamTypeSettingsMap::const_iterator it =
555      request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE);
556  return (it != request_permissions_.end() &&
557          it->second.permission == MEDIA_ALLOWED);
558}
559