blob: 1f01da0b8192f49337c95ce7093801997029b65f [file] [log] [blame]
// 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