| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h" |
| |
| #import <AVFoundation/AVFoundation.h> |
| |
| #include "base/callback.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/logging.h" |
| #include "base/mac/foundation_util.h" |
| #include "base/mac/scoped_cftyperef.h" |
| #include "base/macros.h" |
| #include "base/no_destructor.h" |
| #include "base/task/post_task.h" |
| #include "base/task/task_traits.h" |
| #include "chrome/browser/media/webrtc/media_authorization_wrapper_mac.h" |
| #include "chrome/common/chrome_features.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "media/base/media_switches.h" |
| #include "ui/base/cocoa/permissions_utils.h" |
| |
| namespace system_media_permissions { |
| |
| namespace { |
| |
| bool UsingFakeMediaDevices() { |
| return base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kUseFakeDeviceForMediaStream); |
| } |
| |
| // Pointer to OS call wrapper that tests can set. |
| MediaAuthorizationWrapper* g_media_authorization_wrapper_for_tests = nullptr; |
| |
| // Implementation of OS call wrapper that does the actual OS calls. |
| class MediaAuthorizationWrapperImpl : public MediaAuthorizationWrapper { |
| public: |
| MediaAuthorizationWrapperImpl() = default; |
| ~MediaAuthorizationWrapperImpl() final = default; |
| |
| NSInteger AuthorizationStatusForMediaType(AVMediaType media_type) final { |
| if (@available(macOS 10.14, *)) { |
| return [AVCaptureDevice authorizationStatusForMediaType:media_type]; |
| } else { |
| NOTREACHED(); |
| return 0; |
| } |
| } |
| |
| void RequestAccessForMediaType(AVMediaType media_type, |
| base::OnceClosure callback, |
| const base::TaskTraits& traits) final { |
| if (@available(macOS 10.14, *)) { |
| __block base::OnceClosure block_callback = std::move(callback); |
| [AVCaptureDevice requestAccessForMediaType:media_type |
| completionHandler:^(BOOL granted) { |
| base::PostTask(FROM_HERE, traits, |
| std::move(block_callback)); |
| }]; |
| } else { |
| NOTREACHED(); |
| base::PostTask(FROM_HERE, traits, std::move(callback)); |
| } |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MediaAuthorizationWrapperImpl); |
| }; |
| |
| MediaAuthorizationWrapper& GetMediaAuthorizationWrapper() { |
| if (g_media_authorization_wrapper_for_tests) |
| return *g_media_authorization_wrapper_for_tests; |
| |
| static base::NoDestructor<MediaAuthorizationWrapperImpl> |
| media_authorization_wrapper; |
| return *media_authorization_wrapper; |
| } |
| |
| NSInteger MediaAuthorizationStatus(AVMediaType media_type) { |
| if (@available(macOS 10.14, *)) { |
| return GetMediaAuthorizationWrapper().AuthorizationStatusForMediaType( |
| media_type); |
| } |
| |
| NOTREACHED(); |
| return 0; |
| } |
| |
| SystemPermission CheckSystemMediaCapturePermission(AVMediaType media_type) { |
| if (UsingFakeMediaDevices()) |
| return SystemPermission::kAllowed; |
| |
| if (@available(macOS 10.14, *)) { |
| NSInteger auth_status = MediaAuthorizationStatus(media_type); |
| switch (auth_status) { |
| case AVAuthorizationStatusNotDetermined: |
| return SystemPermission::kNotDetermined; |
| case AVAuthorizationStatusRestricted: |
| return SystemPermission::kRestricted; |
| case AVAuthorizationStatusDenied: |
| return SystemPermission::kDenied; |
| case AVAuthorizationStatusAuthorized: |
| return SystemPermission::kAllowed; |
| default: |
| NOTREACHED(); |
| return SystemPermission::kAllowed; |
| } |
| } |
| |
| // On pre-10.14, there are no system permissions, so we return allowed. |
| return SystemPermission::kAllowed; |
| } |
| |
| void RequestSystemMediaCapturePermission(AVMediaType media_type, |
| base::OnceClosure callback, |
| const base::TaskTraits& traits) { |
| if (UsingFakeMediaDevices()) { |
| base::PostTask(FROM_HERE, traits, std::move(callback)); |
| return; |
| } |
| |
| if (@available(macOS 10.14, *)) { |
| GetMediaAuthorizationWrapper().RequestAccessForMediaType( |
| media_type, std::move(callback), traits); |
| } else { |
| NOTREACHED(); |
| // Should never happen since for pre-10.14 system permissions don't exist |
| // and checking them in CheckSystemAudioCapturePermission() will always |
| // return allowed, and this function should not be called. |
| base::PostTask(FROM_HERE, traits, std::move(callback)); |
| } |
| } |
| |
| // Heuristic to check screen capture permission on macOS 10.15. |
| // Screen Capture is considered allowed if the name of at least one normal |
| // or dock window running on another process is visible. |
| // See https://crbug.com/993692. |
| bool IsScreenCaptureAllowed() { |
| if (@available(macOS 10.15, *)) { |
| if (!base::FeatureList::IsEnabled( |
| features::kMacSystemScreenCapturePermissionCheck)) { |
| return true; |
| } |
| } |
| |
| return ui::IsScreenCaptureAllowed(); |
| } |
| |
| } // namespace |
| |
| SystemPermission CheckSystemAudioCapturePermission() { |
| return CheckSystemMediaCapturePermission(AVMediaTypeAudio); |
| } |
| |
| SystemPermission CheckSystemVideoCapturePermission() { |
| return CheckSystemMediaCapturePermission(AVMediaTypeVideo); |
| } |
| |
| SystemPermission CheckSystemScreenCapturePermission() { |
| return IsScreenCaptureAllowed() ? SystemPermission::kAllowed |
| : SystemPermission::kDenied; |
| } |
| |
| void RequestSystemAudioCapturePermisson(base::OnceClosure callback, |
| const base::TaskTraits& traits) { |
| RequestSystemMediaCapturePermission(AVMediaTypeAudio, std::move(callback), |
| traits); |
| } |
| |
| void RequestSystemVideoCapturePermisson(base::OnceClosure callback, |
| const base::TaskTraits& traits) { |
| RequestSystemMediaCapturePermission(AVMediaTypeVideo, std::move(callback), |
| traits); |
| } |
| |
| void SetMediaAuthorizationWrapperForTesting( |
| MediaAuthorizationWrapper* wrapper) { |
| CHECK(!g_media_authorization_wrapper_for_tests); |
| g_media_authorization_wrapper_for_tests = wrapper; |
| } |
| |
| } // namespace system_media_permissions |