media_stream_devices_controller.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  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. #include "components/webrtc/media_stream_devices_controller.h"
  5. #include <memory>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "build/build_config.h"
  10. #include "components/permissions/permission_util.h"
  11. #include "content/public/browser/browser_context.h"
  12. #include "content/public/browser/permission_controller.h"
  13. #include "content/public/browser/permission_result.h"
  14. #include "content/public/browser/render_frame_host.h"
  15. #include "content/public/browser/render_process_host.h"
  16. #include "content/public/browser/render_widget_host_view.h"
  17. #include "content/public/browser/web_contents.h"
  18. #include "services/network/public/cpp/is_potentially_trustworthy.h"
  19. #include "third_party/blink/public/common/permissions/permission_utils.h"
  20. #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
  21. #include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h"
  22. #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom.h"
  23. #if BUILDFLAG(IS_ANDROID)
  24. #include "components/permissions/android/android_permission_util.h"
  25. #include "ui/android/window_android.h"
  26. #endif
  27. using blink::MediaStreamDevices;
  28. namespace webrtc {
  29. namespace {
  30. // Returns true if the given PermissionType is being requested in
  31. // |request|.
  32. bool PermissionIsRequested(blink::PermissionType permission,
  33. const content::MediaStreamRequest& request) {
  34. if (permission == blink::PermissionType::AUDIO_CAPTURE)
  35. return request.audio_type ==
  36. blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
  37. if (permission == blink::PermissionType::VIDEO_CAPTURE)
  38. return request.video_type ==
  39. blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE;
  40. return false;
  41. }
  42. } // namespace
  43. // static
  44. void MediaStreamDevicesController::RequestPermissions(
  45. const content::MediaStreamRequest& request,
  46. MediaStreamDeviceEnumerator* enumerator,
  47. ResultCallback callback) {
  48. content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
  49. request.render_process_id, request.render_frame_id);
  50. // The RFH may have been destroyed by the time the request is processed.
  51. if (!rfh) {
  52. std::move(callback).Run(
  53. blink::mojom::StreamDevicesSet(),
  54. blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN, false,
  55. {}, {});
  56. return;
  57. }
  58. if (rfh->GetLastCommittedOrigin().GetURL().is_empty()) {
  59. std::move(callback).Run(
  60. blink::mojom::StreamDevicesSet(),
  61. blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, false, {},
  62. {});
  63. return;
  64. }
  65. if (rfh->GetLastCommittedOrigin().GetURL() != request.security_origin) {
  66. std::move(callback).Run(
  67. blink::mojom::StreamDevicesSet(),
  68. blink::mojom::MediaStreamRequestResult::INVALID_SECURITY_ORIGIN, false,
  69. {}, {});
  70. return;
  71. }
  72. content::WebContents* web_contents =
  73. content::WebContents::FromRenderFrameHost(rfh);
  74. std::unique_ptr<MediaStreamDevicesController> controller(
  75. new MediaStreamDevicesController(web_contents, enumerator, request,
  76. std::move(callback)));
  77. std::vector<blink::PermissionType> permission_types;
  78. content::PermissionController* permission_controller =
  79. web_contents->GetBrowserContext()->GetPermissionController();
  80. if (controller->ShouldRequestAudio()) {
  81. content::PermissionResult permission_status =
  82. permission_controller->GetPermissionResultForCurrentDocument(
  83. blink::PermissionType::AUDIO_CAPTURE, rfh);
  84. if (permission_status.status == blink::mojom::PermissionStatus::DENIED) {
  85. controller->denial_reason_ =
  86. blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
  87. controller->RunCallback(permission_status.source ==
  88. content::PermissionStatusSource::FEATURE_POLICY);
  89. return;
  90. }
  91. permission_types.push_back(blink::PermissionType::AUDIO_CAPTURE);
  92. }
  93. if (controller->ShouldRequestVideo()) {
  94. content::PermissionResult permission_status =
  95. permission_controller->GetPermissionResultForCurrentDocument(
  96. blink::PermissionType::VIDEO_CAPTURE, rfh);
  97. if (permission_status.status == blink::mojom::PermissionStatus::DENIED) {
  98. controller->denial_reason_ =
  99. blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
  100. controller->RunCallback(permission_status.source ==
  101. content::PermissionStatusSource::FEATURE_POLICY);
  102. return;
  103. }
  104. permission_types.push_back(blink::PermissionType::VIDEO_CAPTURE);
  105. bool has_pan_tilt_zoom_camera = controller->HasAvailableDevices(
  106. blink::PermissionType::CAMERA_PAN_TILT_ZOOM,
  107. request.requested_video_device_id);
  108. // Request CAMERA_PAN_TILT_ZOOM only if the website requested the
  109. // pan-tilt-zoom permission and there are suitable PTZ capable devices
  110. // available.
  111. if (request.request_pan_tilt_zoom_permission && has_pan_tilt_zoom_camera) {
  112. permission_status =
  113. permission_controller->GetPermissionResultForCurrentDocument(
  114. blink::PermissionType::CAMERA_PAN_TILT_ZOOM, rfh);
  115. if (permission_status.status == blink::mojom::PermissionStatus::DENIED) {
  116. controller->denial_reason_ =
  117. blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
  118. controller->RunCallback(/*blocked_by_permissions_policy=*/false);
  119. return;
  120. }
  121. permission_types.push_back(blink::PermissionType::CAMERA_PAN_TILT_ZOOM);
  122. }
  123. }
  124. // It is OK to ignore `request.security_origin` because it will be calculated
  125. // from `render_frame_host` and we always ignore `requesting_origin` for
  126. // `AUDIO_CAPTURE` and `VIDEO_CAPTURE`.
  127. // `render_frame_host->GetMainFrame()->GetLastCommittedOrigin()` will be used
  128. // instead.
  129. rfh->GetBrowserContext()
  130. ->GetPermissionController()
  131. ->RequestPermissionsFromCurrentDocument(
  132. permission_types, rfh, request.user_gesture,
  133. base::BindOnce(
  134. &MediaStreamDevicesController::PromptAnsweredGroupedRequest,
  135. std::move(controller)));
  136. }
  137. MediaStreamDevicesController::~MediaStreamDevicesController() {
  138. if (!callback_.is_null()) {
  139. std::move(callback_).Run(
  140. blink::mojom::StreamDevicesSet(),
  141. blink::mojom::MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN, false,
  142. {}, {});
  143. }
  144. }
  145. MediaStreamDevicesController::MediaStreamDevicesController(
  146. content::WebContents* web_contents,
  147. MediaStreamDeviceEnumerator* enumerator,
  148. const content::MediaStreamRequest& request,
  149. ResultCallback callback)
  150. : web_contents_(web_contents),
  151. enumerator_(enumerator),
  152. request_(request),
  153. callback_(std::move(callback)) {
  154. DCHECK(network::IsUrlPotentiallyTrustworthy(request_.security_origin) ||
  155. request_.request_type == blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY);
  156. if (!enumerator_)
  157. enumerator_ = &owned_enumerator_;
  158. denial_reason_ = blink::mojom::MediaStreamRequestResult::OK;
  159. audio_setting_ = GetContentSetting(blink::PermissionType::AUDIO_CAPTURE,
  160. request, &denial_reason_);
  161. video_setting_ = GetContentSetting(blink::PermissionType::VIDEO_CAPTURE,
  162. request, &denial_reason_);
  163. }
  164. bool MediaStreamDevicesController::ShouldRequestAudio() const {
  165. return audio_setting_ == CONTENT_SETTING_ASK;
  166. }
  167. bool MediaStreamDevicesController::ShouldRequestVideo() const {
  168. return video_setting_ == CONTENT_SETTING_ASK;
  169. }
  170. blink::mojom::StreamDevicesSetPtr MediaStreamDevicesController::GetDevices(
  171. ContentSetting audio_setting,
  172. ContentSetting video_setting) {
  173. bool audio_allowed = audio_setting == CONTENT_SETTING_ALLOW;
  174. bool video_allowed = video_setting == CONTENT_SETTING_ALLOW;
  175. blink::mojom::StreamDevicesSetPtr stream_devices_set =
  176. blink::mojom::StreamDevicesSet::New();
  177. if (!audio_allowed && !video_allowed)
  178. return nullptr;
  179. // TODO(crbug.com/1300883): Generalize to multiple streams.
  180. stream_devices_set->stream_devices.emplace_back(
  181. blink::mojom::StreamDevices::New());
  182. blink::mojom::StreamDevices& devices = *stream_devices_set->stream_devices[0];
  183. switch (request_.request_type) {
  184. case blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY: {
  185. // For open device request, when requested device_id is empty, pick
  186. // the first available of the given type. If requested device_id is
  187. // not empty, return the desired device if it's available. Otherwise,
  188. // return no device.
  189. if (audio_allowed &&
  190. request_.audio_type ==
  191. blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
  192. DCHECK_EQ(blink::mojom::MediaStreamType::NO_SERVICE,
  193. request_.video_type);
  194. if (!request_.requested_audio_device_id.empty()) {
  195. devices.audio_device = *enumerator_->GetRequestedAudioDevice(
  196. request_.requested_audio_device_id);
  197. } else {
  198. const blink::MediaStreamDevices& audio_devices =
  199. enumerator_->GetAudioCaptureDevices();
  200. if (!audio_devices.empty())
  201. devices.audio_device = audio_devices.front();
  202. }
  203. } else if (video_allowed &&
  204. request_.video_type ==
  205. blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
  206. DCHECK_EQ(blink::mojom::MediaStreamType::NO_SERVICE,
  207. request_.audio_type);
  208. // Pepper API opens only one device at a time.
  209. if (!request_.requested_video_device_id.empty()) {
  210. devices.video_device = *enumerator_->GetRequestedVideoDevice(
  211. request_.requested_video_device_id);
  212. } else {
  213. const blink::MediaStreamDevices& video_devices =
  214. enumerator_->GetVideoCaptureDevices();
  215. if (!video_devices.empty())
  216. devices.video_device = video_devices.front();
  217. }
  218. }
  219. break;
  220. }
  221. case blink::MEDIA_GENERATE_STREAM: {
  222. bool get_default_audio_device = audio_allowed;
  223. bool get_default_video_device = video_allowed;
  224. // Get the exact audio or video device if an id is specified.
  225. if (audio_allowed && !request_.requested_audio_device_id.empty()) {
  226. const blink::MediaStreamDevice* audio_device =
  227. enumerator_->GetRequestedAudioDevice(
  228. request_.requested_audio_device_id);
  229. if (audio_device) {
  230. devices.audio_device = *audio_device;
  231. get_default_audio_device = false;
  232. }
  233. }
  234. if (video_allowed && !request_.requested_video_device_id.empty()) {
  235. const blink::MediaStreamDevice* video_device =
  236. enumerator_->GetRequestedVideoDevice(
  237. request_.requested_video_device_id);
  238. if (video_device) {
  239. devices.video_device = *video_device;
  240. get_default_video_device = false;
  241. }
  242. }
  243. // If either or both audio and video devices were requested but not
  244. // specified by id, get the default devices.
  245. if (get_default_audio_device || get_default_video_device) {
  246. enumerator_->GetDefaultDevicesForBrowserContext(
  247. web_contents_->GetBrowserContext(), get_default_audio_device,
  248. get_default_video_device, devices);
  249. }
  250. break;
  251. }
  252. case blink::MEDIA_GET_OPEN_DEVICE: {
  253. // Transferred tracks, that use blink::MEDIA_GET_OPEN_DEVICE type, do not
  254. // need to get permissions for MediaStreamDevice as those are controlled
  255. // by the original context.
  256. NOTREACHED();
  257. break;
  258. }
  259. case blink::MEDIA_DEVICE_ACCESS: {
  260. // Get the default devices for the request.
  261. enumerator_->GetDefaultDevicesForBrowserContext(
  262. web_contents_->GetBrowserContext(), audio_allowed, video_allowed,
  263. devices);
  264. break;
  265. }
  266. case blink::MEDIA_DEVICE_UPDATE: {
  267. NOTREACHED();
  268. break;
  269. }
  270. } // switch
  271. return stream_devices_set;
  272. }
  273. void MediaStreamDevicesController::RunCallback(
  274. bool blocked_by_permissions_policy) {
  275. CHECK(callback_);
  276. blink::mojom::StreamDevicesSetPtr stream_devices_set;
  277. // If all requested permissions are allowed then the callback should report
  278. // success, otherwise we report |denial_reason_|.
  279. blink::mojom::MediaStreamRequestResult request_result =
  280. blink::mojom::MediaStreamRequestResult::OK;
  281. if ((audio_setting_ == CONTENT_SETTING_ALLOW ||
  282. audio_setting_ == CONTENT_SETTING_DEFAULT) &&
  283. (video_setting_ == CONTENT_SETTING_ALLOW ||
  284. video_setting_ == CONTENT_SETTING_DEFAULT)) {
  285. stream_devices_set = GetDevices(audio_setting_, video_setting_);
  286. if (stream_devices_set->stream_devices.empty()) {
  287. // Even if all requested permissions are allowed, if there are no devices
  288. // at this point we still report a failure.
  289. request_result = blink::mojom::MediaStreamRequestResult::NO_HARDWARE;
  290. }
  291. } else {
  292. DCHECK_NE(blink::mojom::MediaStreamRequestResult::OK, denial_reason_);
  293. request_result = denial_reason_;
  294. stream_devices_set = blink::mojom::StreamDevicesSet::New();
  295. }
  296. std::move(callback_).Run(*stream_devices_set, request_result,
  297. blocked_by_permissions_policy, audio_setting_,
  298. video_setting_);
  299. }
  300. ContentSetting MediaStreamDevicesController::GetContentSetting(
  301. blink::PermissionType permission,
  302. const content::MediaStreamRequest& request,
  303. blink::mojom::MediaStreamRequestResult* denial_reason) const {
  304. DCHECK(permission == blink::PermissionType::AUDIO_CAPTURE ||
  305. permission == blink::PermissionType::VIDEO_CAPTURE);
  306. DCHECK(!request_.security_origin.is_empty());
  307. DCHECK(network::IsUrlPotentiallyTrustworthy(request_.security_origin) ||
  308. request_.request_type == blink::MEDIA_OPEN_DEVICE_PEPPER_ONLY);
  309. if (!PermissionIsRequested(permission, request)) {
  310. // No denial reason set as it will have been previously set.
  311. return CONTENT_SETTING_DEFAULT;
  312. }
  313. std::string device_id;
  314. if (permission == blink::PermissionType::AUDIO_CAPTURE)
  315. device_id = request.requested_audio_device_id;
  316. else
  317. device_id = request.requested_video_device_id;
  318. if (!HasAvailableDevices(permission, device_id)) {
  319. *denial_reason = blink::mojom::MediaStreamRequestResult::NO_HARDWARE;
  320. return CONTENT_SETTING_BLOCK;
  321. }
  322. if (!IsUserAcceptAllowed(permission)) {
  323. *denial_reason = blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
  324. return CONTENT_SETTING_BLOCK;
  325. }
  326. // Don't request if the kill switch is on.
  327. if (PermissionIsBlockedForReason(
  328. permission, content::PermissionStatusSource::KILL_SWITCH)) {
  329. *denial_reason = blink::mojom::MediaStreamRequestResult::KILL_SWITCH_ON;
  330. return CONTENT_SETTING_BLOCK;
  331. }
  332. return CONTENT_SETTING_ASK;
  333. }
  334. bool MediaStreamDevicesController::IsUserAcceptAllowed(
  335. blink::PermissionType permission) const {
  336. #if BUILDFLAG(IS_ANDROID)
  337. ui::WindowAndroid* window_android =
  338. web_contents_->GetNativeView()->GetWindowAndroid();
  339. if (!window_android)
  340. return false;
  341. ContentSettingsType content_type;
  342. switch (permission) {
  343. case blink::PermissionType::AUDIO_CAPTURE:
  344. content_type = ContentSettingsType::MEDIASTREAM_MIC;
  345. break;
  346. case blink::PermissionType::VIDEO_CAPTURE:
  347. content_type = ContentSettingsType::MEDIASTREAM_CAMERA;
  348. break;
  349. default:
  350. NOTREACHED();
  351. return false;
  352. }
  353. std::vector<std::string> required_android_permissions;
  354. permissions::AppendRequiredAndroidPermissionsForContentSetting(
  355. content_type, &required_android_permissions);
  356. for (const auto& android_permission : required_android_permissions) {
  357. if (!window_android->HasPermission(android_permission) &&
  358. !window_android->CanRequestPermission(android_permission)) {
  359. return false;
  360. }
  361. }
  362. // Don't approve device requests if the tab was hidden.
  363. // TODO(qinmin): Add a test for this. http://crbug.com/396869.
  364. // TODO(raymes): Shouldn't this apply to all permissions not just audio/video?
  365. return web_contents_->GetRenderWidgetHostView()->IsShowing();
  366. #else
  367. return true;
  368. #endif
  369. }
  370. bool MediaStreamDevicesController::PermissionIsBlockedForReason(
  371. blink::PermissionType permission,
  372. content::PermissionStatusSource reason) const {
  373. content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
  374. request_.render_process_id, request_.render_frame_id);
  375. if (rfh->GetLastCommittedOrigin().GetURL() != request_.security_origin) {
  376. return false;
  377. }
  378. // TODO(raymes): This function wouldn't be needed if
  379. // PermissionManager::RequestPermissions returned a denial reason.
  380. content::PermissionResult result =
  381. web_contents_->GetBrowserContext()
  382. ->GetPermissionController()
  383. ->GetPermissionResultForCurrentDocument(permission, rfh);
  384. if (result.source == reason) {
  385. DCHECK_EQ(blink::mojom::PermissionStatus::DENIED, result.status);
  386. return true;
  387. }
  388. return false;
  389. }
  390. void MediaStreamDevicesController::PromptAnsweredGroupedRequest(
  391. const std::vector<blink::mojom::PermissionStatus>& permissions_status) {
  392. if (content::RenderFrameHost::FromID(request_.render_process_id,
  393. request_.render_frame_id) == nullptr) {
  394. // The frame requesting media devices was removed while we were waiting for
  395. // a user response on permissions. Nothing more to do.
  396. return;
  397. }
  398. std::vector<ContentSetting> responses;
  399. std::transform(permissions_status.begin(), permissions_status.end(),
  400. back_inserter(responses),
  401. permissions::PermissionUtil::PermissionStatusToContentSetting);
  402. bool need_audio = ShouldRequestAudio();
  403. bool need_video = ShouldRequestVideo();
  404. bool blocked_by_permissions_policy = need_audio || need_video;
  405. // The audio setting will always be the first one in the vector, if it was
  406. // requested.
  407. if (need_audio) {
  408. audio_setting_ = responses.front();
  409. blocked_by_permissions_policy &=
  410. audio_setting_ == CONTENT_SETTING_BLOCK &&
  411. PermissionIsBlockedForReason(
  412. blink::PermissionType::AUDIO_CAPTURE,
  413. content::PermissionStatusSource::FEATURE_POLICY);
  414. }
  415. if (need_video) {
  416. video_setting_ = responses.at(need_audio ? 1 : 0);
  417. blocked_by_permissions_policy &=
  418. video_setting_ == CONTENT_SETTING_BLOCK &&
  419. PermissionIsBlockedForReason(
  420. blink::PermissionType::VIDEO_CAPTURE,
  421. content::PermissionStatusSource::FEATURE_POLICY);
  422. }
  423. for (ContentSetting response : responses) {
  424. if (response == CONTENT_SETTING_BLOCK)
  425. denial_reason_ =
  426. blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED;
  427. else if (response == CONTENT_SETTING_ASK)
  428. denial_reason_ =
  429. blink::mojom::MediaStreamRequestResult::PERMISSION_DISMISSED;
  430. }
  431. RunCallback(blocked_by_permissions_policy);
  432. }
  433. bool MediaStreamDevicesController::HasAvailableDevices(
  434. blink::PermissionType permission,
  435. const std::string& device_id) const {
  436. const MediaStreamDevices* devices = nullptr;
  437. if (permission == blink::PermissionType::AUDIO_CAPTURE) {
  438. devices = &enumerator_->GetAudioCaptureDevices();
  439. } else if (permission == blink::PermissionType::VIDEO_CAPTURE ||
  440. permission == blink::PermissionType::CAMERA_PAN_TILT_ZOOM) {
  441. devices = &enumerator_->GetVideoCaptureDevices();
  442. } else {
  443. NOTREACHED();
  444. }
  445. // TODO(tommi): It's kind of strange to have this here since if we fail this
  446. // test, there'll be a UI shown that indicates to the user that access to
  447. // non-existing audio/video devices has been denied. The user won't have
  448. // any way to change that but there will be a UI shown which indicates that
  449. // access is blocked.
  450. if (devices->empty())
  451. return false;
  452. // If there are no particular device requirements, all devices will do.
  453. if (device_id.empty() &&
  454. permission != blink::PermissionType::CAMERA_PAN_TILT_ZOOM) {
  455. return true;
  456. }
  457. // Try to find a device which fulfils all device requirements.
  458. for (const blink::MediaStreamDevice& device : *devices) {
  459. if (!device_id.empty() && device.id != device_id) {
  460. continue;
  461. }
  462. if (permission == blink::PermissionType::CAMERA_PAN_TILT_ZOOM &&
  463. !device.video_control_support.pan &&
  464. !device.video_control_support.tilt &&
  465. !device.video_control_support.zoom) {
  466. continue;
  467. }
  468. return true;
  469. }
  470. return false;
  471. }
  472. } // namespace webrtc