Commit b07ece8e authored by Yuwei Huang's avatar Yuwei Huang Committed by Chromium LUCI CQ

[remoting host][mac] Explicitly request audio capture permission

Looks like calls to AudioQueue APIs don't always trigger audio capture
permission request, e.g. it doesn't seem to pop up the permission dialog
after I reset microphone permission with `tccutil reset Microphone`.
Without audio capture permission, calls to AudioQueue APIs will still
succeed as if everything works, but what we get will be empty buffers.

Bug: 1168729
Change-Id: I5491e420fb907a1e438f57ef172ecb997f99e6c2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2641145Reviewed-by: default avatarJoe Downing <joedow@chromium.org>
Commit-Queue: Joe Downing <joedow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845743}
parent e4731a65
......@@ -429,6 +429,7 @@ static_library("common") {
deps += [
":remoting_version",
"//remoting/host/mac:constants",
"//remoting/host/mac:permission_checking",
]
}
......
......@@ -17,6 +17,7 @@
#include "remoting/base/logging.h"
#include "remoting/host/host_setting_keys.h"
#include "remoting/host/host_settings.h"
#include "remoting/host/mac/permission_utils.h"
#include "remoting/proto/audio.pb.h"
namespace remoting {
......@@ -266,6 +267,21 @@ bool AudioCapturerMac::StartInputQueue() {
DCHECK(!input_queue_);
DCHECK(!is_started_);
if (mac::CanCaptureAudio()) {
HOST_LOG << "Audio capture is allowed.";
} else {
HOST_LOG << "We have no audio capture permission. Requesting one...";
mac::RequestAudioCapturePermission(base::BindOnce([](bool granted) {
// We don't need to defer the AudioQueue setup process as the buffers will
// start being filled up immediately after the user approves the request.
if (granted) {
HOST_LOG << "Audio capture permission granted.";
} else {
LOG(ERROR) << "Audio capture permission not granted.";
}
}));
}
// Setup input queue.
// This runs on AudioQueue's internal thread. For some reason if we specify
// inCallbackRunLoop to current thread, then the callback will never get
......
......@@ -44,6 +44,8 @@ source_set("permission_checking") {
":constants",
"//remoting/resources",
]
frameworks = [ "AVFoundation.framework" ]
}
executable("remoting_me2me_host_service") {
......
......@@ -5,6 +5,7 @@
#ifndef REMOTING_HOST_MAC_PERMISSION_UTILS_H_
#define REMOTING_HOST_MAC_PERMISSION_UTILS_H_
#include "base/callback.h"
#include "base/memory/scoped_refptr.h"
namespace base {
......@@ -34,6 +35,22 @@ bool CanRecordScreen();
void PromptUserToChangeTrustStateIfNeeded(
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
// Returns true if the current process has been granted permission to capture
// audio. Unlike the other functions above, this function has no side effect and
// will not request audio capture permission. To do so, call
// RequestAudioCapturePermission() instead.
bool CanCaptureAudio();
// Request audio capture permission. This will add an entry to the System
// Preference's Microphone pane (if it doesn't exist already) and it may pop up
// a system dialog informing the user that this app is requesting permission.
// You may need to explicitly check and request audio capture permission, as
// calling AudioQueue APIs doesn't always trigger the permission request.
// |callback| will be run on the caller's sequence with the grant result. True
// if granted, false otherwise.
// ONLY call this function when CanCaptureAudio() returns false.
void RequestAudioCapturePermission(base::OnceCallback<void(bool)> callback);
} // namespace mac
} // namespace remoting
......
......@@ -4,6 +4,7 @@
#import "remoting/host/mac/permission_utils.h"
#import <AVFoundation/AVFoundation.h>
#import <Cocoa/Cocoa.h>
#include "base/bind.h"
......@@ -16,6 +17,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "remoting/base/string_resources.h"
#include "ui/base/cocoa/permissions_utils.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -160,5 +162,31 @@ void PromptUserToChangeTrustStateIfNeeded(
PromptUserForScreenRecordingPermissionIfNeeded(task_runner);
}
bool CanCaptureAudio() {
if (@available(macOS 10.14, *)) {
NSInteger auth_status =
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
return auth_status == AVAuthorizationStatusAuthorized;
}
return true;
}
void RequestAudioCapturePermission(base::OnceCallback<void(bool)> callback) {
if (@available(macOS 10.14, *)) {
auto task_runner = base::SequencedTaskRunnerHandle::Get();
__block auto block_callback = std::move(callback);
[AVCaptureDevice
requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
task_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(block_callback), granted));
}];
return;
}
// CanCaptureAudio() returns true for older OSes.
NOTREACHED();
}
} // namespace mac
} // namespace remoting
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment