1// Copyright 2019 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/webrtc/system_media_capture_permissions_mac.h" 6 7#import <AVFoundation/AVFoundation.h> 8 9#include "base/callback.h" 10#include "base/callback_helpers.h" 11#include "base/command_line.h" 12#include "base/feature_list.h" 13#include "base/logging.h" 14#include "base/mac/foundation_util.h" 15#include "base/mac/scoped_cftyperef.h" 16#include "base/macros.h" 17#include "base/no_destructor.h" 18#include "base/task/post_task.h" 19#include "base/task/task_traits.h" 20#include "chrome/browser/media/webrtc/media_authorization_wrapper_mac.h" 21#include "chrome/common/chrome_features.h" 22#include "content/public/browser/browser_task_traits.h" 23#include "content/public/browser/browser_thread.h" 24#include "media/base/media_switches.h" 25#include "ui/base/cocoa/permissions_utils.h" 26 27namespace system_media_permissions { 28 29namespace { 30 31bool UsingFakeMediaDevices() { 32 return base::CommandLine::ForCurrentProcess()->HasSwitch( 33 switches::kUseFakeDeviceForMediaStream); 34} 35 36// Pointer to OS call wrapper that tests can set. 37MediaAuthorizationWrapper* g_media_authorization_wrapper_for_tests = nullptr; 38 39// Implementation of OS call wrapper that does the actual OS calls. 40class MediaAuthorizationWrapperImpl : public MediaAuthorizationWrapper { 41 public: 42 MediaAuthorizationWrapperImpl() = default; 43 ~MediaAuthorizationWrapperImpl() final = default; 44 45 NSInteger AuthorizationStatusForMediaType(AVMediaType media_type) final { 46 if (@available(macOS 10.14, *)) { 47 return [AVCaptureDevice authorizationStatusForMediaType:media_type]; 48 } else { 49 NOTREACHED(); 50 return 0; 51 } 52 } 53 54 void RequestAccessForMediaType(AVMediaType media_type, 55 base::RepeatingClosure callback, 56 const base::TaskTraits& traits) final { 57 if (@available(macOS 10.14, *)) { 58 [AVCaptureDevice 59 requestAccessForMediaType:media_type 60 completionHandler:^(BOOL granted) { 61 base::PostTask(FROM_HERE, traits, std::move(callback)); 62 }]; 63 } else { 64 NOTREACHED(); 65 base::PostTask(FROM_HERE, traits, std::move(callback)); 66 } 67 } 68 69 private: 70 DISALLOW_COPY_AND_ASSIGN(MediaAuthorizationWrapperImpl); 71}; 72 73MediaAuthorizationWrapper& GetMediaAuthorizationWrapper() { 74 if (g_media_authorization_wrapper_for_tests) 75 return *g_media_authorization_wrapper_for_tests; 76 77 static base::NoDestructor<MediaAuthorizationWrapperImpl> 78 media_authorization_wrapper; 79 return *media_authorization_wrapper; 80} 81 82NSInteger MediaAuthorizationStatus(AVMediaType media_type) { 83 if (@available(macOS 10.14, *)) { 84 return GetMediaAuthorizationWrapper().AuthorizationStatusForMediaType( 85 media_type); 86 } 87 88 NOTREACHED(); 89 return 0; 90} 91 92SystemPermission CheckSystemMediaCapturePermission(AVMediaType media_type) { 93 if (UsingFakeMediaDevices()) 94 return SystemPermission::kAllowed; 95 96 if (@available(macOS 10.14, *)) { 97 NSInteger auth_status = MediaAuthorizationStatus(media_type); 98 switch (auth_status) { 99 case AVAuthorizationStatusNotDetermined: 100 return SystemPermission::kNotDetermined; 101 case AVAuthorizationStatusRestricted: 102 return SystemPermission::kRestricted; 103 case AVAuthorizationStatusDenied: 104 return SystemPermission::kDenied; 105 case AVAuthorizationStatusAuthorized: 106 return SystemPermission::kAllowed; 107 default: 108 NOTREACHED(); 109 return SystemPermission::kAllowed; 110 } 111 } 112 113 // On pre-10.14, there are no system permissions, so we return allowed. 114 return SystemPermission::kAllowed; 115} 116 117// Use RepeatingCallback since it must be copyable for use in the block. It's 118// only called once though. 119void RequestSystemMediaCapturePermission(AVMediaType media_type, 120 base::RepeatingClosure callback, 121 const base::TaskTraits& traits) { 122 if (UsingFakeMediaDevices()) { 123 base::PostTask(FROM_HERE, traits, std::move(callback)); 124 return; 125 } 126 127 if (@available(macOS 10.14, *)) { 128 GetMediaAuthorizationWrapper().RequestAccessForMediaType( 129 media_type, std::move(callback), traits); 130 } else { 131 NOTREACHED(); 132 // Should never happen since for pre-10.14 system permissions don't exist 133 // and checking them in CheckSystemAudioCapturePermission() will always 134 // return allowed, and this function should not be called. 135 base::PostTask(FROM_HERE, traits, std::move(callback)); 136 } 137} 138 139// Heuristic to check screen capture permission on macOS 10.15. 140// Screen Capture is considered allowed if the name of at least one normal 141// or dock window running on another process is visible. 142// See https://crbug.com/993692. 143bool IsScreenCaptureAllowed() { 144 if (@available(macOS 10.15, *)) { 145 if (!base::FeatureList::IsEnabled( 146 features::kMacSystemScreenCapturePermissionCheck)) { 147 return true; 148 } 149 } 150 151 return ui::IsScreenCaptureAllowed(); 152} 153 154} // namespace 155 156SystemPermission CheckSystemAudioCapturePermission() { 157 return CheckSystemMediaCapturePermission(AVMediaTypeAudio); 158} 159 160SystemPermission CheckSystemVideoCapturePermission() { 161 return CheckSystemMediaCapturePermission(AVMediaTypeVideo); 162} 163 164SystemPermission CheckSystemScreenCapturePermission() { 165 return IsScreenCaptureAllowed() ? SystemPermission::kAllowed 166 : SystemPermission::kDenied; 167} 168 169void RequestSystemAudioCapturePermisson(base::OnceClosure callback, 170 const base::TaskTraits& traits) { 171 RequestSystemMediaCapturePermission( 172 AVMediaTypeAudio, base::AdaptCallbackForRepeating(std::move(callback)), 173 traits); 174} 175 176void RequestSystemVideoCapturePermisson(base::OnceClosure callback, 177 const base::TaskTraits& traits) { 178 RequestSystemMediaCapturePermission( 179 AVMediaTypeVideo, base::AdaptCallbackForRepeating(std::move(callback)), 180 traits); 181} 182 183void SetMediaAuthorizationWrapperForTesting( 184 MediaAuthorizationWrapper* wrapper) { 185 CHECK(!g_media_authorization_wrapper_for_tests); 186 g_media_authorization_wrapper_for_tests = wrapper; 187} 188 189} // namespace system_media_permissions 190