apple_authorization.cpp
1 // Copyright 2023 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #include <future> 6 #include <objc/message.h> 7 #include "common/apple_authorization.h" 8 #include "common/logging/log.h" 9 10 namespace AppleAuthorization { 11 12 // Bindings to Objective-C APIs 13 14 using NSString = void; 15 using AVMediaType = NSString*; 16 enum AVAuthorizationStatus : int { 17 AVAuthorizationStatusNotDetermined = 0, 18 AVAuthorizationStatusRestricted, 19 AVAuthorizationStatusDenied, 20 AVAuthorizationStatusAuthorized, 21 }; 22 23 typedef NSString* (*send_stringWithUTF8String)(Class, SEL, const char*); 24 typedef AVAuthorizationStatus (*send_authorizationStatusForMediaType)(Class, SEL, AVMediaType); 25 typedef void (*send_requestAccessForMediaType_completionHandler)(Class, SEL, AVMediaType, 26 void (^callback)(bool)); 27 28 NSString* StringToNSString(const std::string_view string) { 29 return reinterpret_cast<send_stringWithUTF8String>(objc_msgSend)( 30 objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), string.data()); 31 } 32 33 AVAuthorizationStatus GetAuthorizationStatus(AVMediaType media_type) { 34 return reinterpret_cast<send_authorizationStatusForMediaType>(objc_msgSend)( 35 objc_getClass("AVCaptureDevice"), sel_registerName("authorizationStatusForMediaType:"), 36 media_type); 37 } 38 39 void RequestAccess(AVMediaType media_type, void (^callback)(bool)) { 40 reinterpret_cast<send_requestAccessForMediaType_completionHandler>(objc_msgSend)( 41 objc_getClass("AVCaptureDevice"), 42 sel_registerName("requestAccessForMediaType:completionHandler:"), media_type, callback); 43 } 44 45 static AVMediaType AVMediaTypeAudio = StringToNSString("soun"); 46 static AVMediaType AVMediaTypeVideo = StringToNSString("vide"); 47 48 // Authorization Logic 49 50 bool CheckAuthorization(AVMediaType type, const std::string_view& type_name) { 51 switch (GetAuthorizationStatus(type)) { 52 case AVAuthorizationStatusNotDetermined: { 53 LOG_INFO(Frontend, "Requesting {} permission.", type_name); 54 __block std::promise<bool> authorization_promise; 55 std::future<bool> authorization_future = authorization_promise.get_future(); 56 RequestAccess(type, ^(bool granted) { 57 LOG_INFO(Frontend, "{} permission request result: {}", type_name, granted); 58 authorization_promise.set_value(granted); 59 }); 60 return authorization_future.get(); 61 } 62 case AVAuthorizationStatusAuthorized: 63 return true; 64 case AVAuthorizationStatusDenied: 65 LOG_WARNING(Frontend, 66 "{} permission has been denied and must be enabled via System Settings.", 67 type_name); 68 return false; 69 case AVAuthorizationStatusRestricted: 70 LOG_WARNING(Frontend, "{} permission is restricted by the system.", type_name); 71 return false; 72 } 73 } 74 75 bool CheckAuthorizationForCamera() { 76 return CheckAuthorization(AVMediaTypeVideo, "Camera"); 77 } 78 79 bool CheckAuthorizationForMicrophone() { 80 return CheckAuthorization(AVMediaTypeAudio, "Microphone"); 81 } 82 83 } // namespace AppleAuthorization