/ src / common / apple_authorization.cpp
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