// Copyright 2023 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include "common/apple_authorization.h" #include "common/logging/log.h" namespace AppleAuthorization { // Bindings to Objective-C APIs using NSString = void; using AVMediaType = NSString*; enum AVAuthorizationStatus : int { AVAuthorizationStatusNotDetermined = 0, AVAuthorizationStatusRestricted, AVAuthorizationStatusDenied, AVAuthorizationStatusAuthorized, }; typedef NSString* (*send_stringWithUTF8String)(Class, SEL, const char*); typedef AVAuthorizationStatus (*send_authorizationStatusForMediaType)(Class, SEL, AVMediaType); typedef void (*send_requestAccessForMediaType_completionHandler)(Class, SEL, AVMediaType, void (^callback)(bool)); NSString* StringToNSString(const std::string_view string) { return reinterpret_cast(objc_msgSend)( objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), string.data()); } AVAuthorizationStatus GetAuthorizationStatus(AVMediaType media_type) { return reinterpret_cast(objc_msgSend)( objc_getClass("AVCaptureDevice"), sel_registerName("authorizationStatusForMediaType:"), media_type); } void RequestAccess(AVMediaType media_type, void (^callback)(bool)) { reinterpret_cast(objc_msgSend)( objc_getClass("AVCaptureDevice"), sel_registerName("requestAccessForMediaType:completionHandler:"), media_type, callback); } static AVMediaType AVMediaTypeAudio = StringToNSString("soun"); static AVMediaType AVMediaTypeVideo = StringToNSString("vide"); // Authorization Logic bool CheckAuthorization(AVMediaType type, const std::string_view& type_name) { switch (GetAuthorizationStatus(type)) { case AVAuthorizationStatusNotDetermined: { LOG_INFO(Frontend, "Requesting {} permission.", type_name); __block std::promise authorization_promise; std::future authorization_future = authorization_promise.get_future(); RequestAccess(type, ^(bool granted) { LOG_INFO(Frontend, "{} permission request result: {}", type_name, granted); authorization_promise.set_value(granted); }); return authorization_future.get(); } case AVAuthorizationStatusAuthorized: return true; case AVAuthorizationStatusDenied: LOG_WARNING(Frontend, "{} permission has been denied and must be enabled via System Settings.", type_name); return false; case AVAuthorizationStatusRestricted: LOG_WARNING(Frontend, "{} permission is restricted by the system.", type_name); return false; } } bool CheckAuthorizationForCamera() { return CheckAuthorization(AVMediaTypeVideo, "Camera"); } bool CheckAuthorizationForMicrophone() { return CheckAuthorization(AVMediaTypeAudio, "Microphone"); } } // namespace AppleAuthorization