我是开发用于飞行模拟的应用程序的团队的一部分。这些应用程序之一也正在MacOS上运行,并且需要访问麦克风才能与在线虚拟空中交通管制进行通信。 从MacOS 10.14开始,麦克风访问不再起作用。它曾经可以在任何早期版本的MacOS中完美运行。我已经读过,从10.14开始,MacOS会询问用户权限,但是此对话框永远不会出现。使用portaudio作为音频库,可以成功打开音频输入流。没有警告,没有错误,也没有指出问题。它只是不返回任何音频输入。
我了解到,许多其他项目(甚至是商业项目)也存在类似的问题。但是我找不到他们最终如何解决的。 我知道该应用捆绑包需要在Info.plist中添加特定键
<key>NSMicrophoneUsageDescription</key>
<string>This application needs access to your Microphone virtual ATC.</string>
但这没有帮助。
其他人则建议添加<key>CFBundleDisplayName</key>
可以解决此问题。但事实并非如此。
值得一提的是,该应用程序未签名。这是一个业余时间的业余项目,我不愿意为苹果的代码签名过程每年花费99美元。可能是罪魁祸首吗?
欢迎任何建议或想法。
作为临时的解决方法,我们告诉用户通过控制台从应用程序捆绑包中启动二进制文件,从而解决了该问题。但是我也想为应用程序捆绑包本身正确修复它。
答案 0 :(得分:0)
对我有用的解决方案是reset PRAM。其中存储了一些系统设置。
在启动过程中,按住Command + Option + P +R。计算机将重新启动,并且当您第二次听到启动声音时,您可能会松开。
再次运行该应用,然后将显示权限对话框。
答案 1 :(得分:0)
从10.14开始,MacOS将询问用户权限,但此对话框永远不会出现
这正是我的问题。 Mac Mojave和Catalina中有一个严重的错误。
就我而言,我的客户在Catalina上面临此问题。我通过JNLP启动JAR。由于MacOS最新版本中与安全相关的更改,应用程序应该获得访问麦克风,屏幕记录,全盘访问等的权限。理想情况下,对于Java应用程序(通过JNLP运行),应该寻求许可的是Java。但是,这不会发生。我的用户没有看到询问麦克风许可的对话框。他们甚至尝试使用最新的Java版本8。仍然没有运气。我挣扎了很多天。最后,这对我有用:
我检测OS是否为MacOS Cataline,如果是,我再次使用javaws启动同一JNLP。为了避免递归,只有在我第一次检测到applet时才执行此操作。这是代码:
以下是完整的代码:
private boolean IsAlreadyRunning()
{
System.out.println("Checking if applet already running by opening applet locked file");
try
{
file_locked_by_applet=new File("my_java_application.lock");
// createNewFile atomically creates a new, empty file ... if and only if a file with this name does not yet exist.
System.out.println("Locked file path: " + file_locked_by_applet.getAbsolutePath());
if (file_locked_by_applet.createNewFile())
{
System.out.println("Opened applet locked file successfully");
file_locked_by_applet.deleteOnExit();
return false;
}
System.out.println("Cannot open applet locked file. Applet might be already running.");
return true;
}
catch (IOException e)
{
System.out.println("Exception while opening applet locked file. Applet might be already running.");
e.printStackTrace();
return true;
}
}
private boolean IsOSMacCatalina()
{
System.out.println("Checking if current operating system is MacOS Catalina");
String OS = System.getProperty("os.name").toLowerCase();
String OSVersion = System.getProperty("os.version").toLowerCase();
String OSArch = System.getProperty("os.arch").toLowerCase();
System.out.println("OS detected: " + OS);
System.out.println("OS version detected: " + OSVersion);
System.out.println("OS arch detected: " + OSArch);
if (OS.contains ("mac os") && OSVersion.contains("10.15"))
{
System.out.println("Operating system: Mac Catalina detected");
return true;
}
System.out.println("Operating system is not Mac Catalina");
return false;
}
// Method that first gets invoked by applet at the beginning
public void start()
{
super.start();
System.out.println("Starting applet here");
System.out.println("JNLP file name: " + System.getProperty("jnlpx.origFilenameArg"));
System.out.println("JVM command line: " + ManagementFactory.getRuntimeMXBean().getInputArguments());
if ((!IsOSMacCatalina()) || IsAlreadyRunning())
{
System.out.println("Either OS is not Catalina or applet is already launched with bash and javaws. Continuing with applet...");
}
else
{
try
{
System.out.println("Applet running first time on Mac Catalina. Starting again with bash and javaws");
// "javaws -wait" causes javaws to start java process and wait for it to exit
String javawsCommandLine = "javaws -wait \"" + System.getProperty("jnlpx.origFilenameArg").replace("\\","/") + "\"";
System.out.println("bash javaws command line to run: " + javawsCommandLine);
// String[] args = new String[] {"bash", "-c", javawsCommandLine}; // Works on Windows where Bash is installed
String[] args = new String[] {"/bin/bash", "-c", javawsCommandLine};
System.out.println("---\nStarting bash javaws process withh args:");
for (String arg: args)
System.out.println(arg);
System.out.println("\n---");
// Runtime.getRuntime() discouraged. Hence we using ProcessBuilder
// Process proc = Runtime.getRuntime().exec("bash -c \"" + javawsCommandLine + "\"");
Process proc = new ProcessBuilder(args).start();
System.out.println("Waiting for bash process to finish");
proc.waitFor();
System.out.println("Bash process finished. Deleting instance locked file");
file_locked_by_applet.delete();
System.out.println("Stopping applet here");
}
catch (java.io.IOException e)
{
e.printStackTrace();
}
catch (java.lang.InterruptedException e)
{
e.printStackTrace();
}
return;
}
答案 2 :(得分:0)
我最后使用了以下两个片段。第一个是积极询问该应用程序是否具有访问权限。如果无法确定(因为之前从未询问过用户),则调用requestAccess()
以主动询问用户并异步处理响应。这两个函数都处理目标MacOS为<10.14的情况,在这种情况下,假定授权为真。
CMacOSMicrophoneAccess::AuthorizationStatus CMacOSMicrophoneAccess::getAuthorizationStatus()
{
if (@available(macOS 10.14, *))
{
NSString *mediaType = AVMediaTypeAudio;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
if (authStatus == AVAuthorizationStatusAuthorized)
{
return AuthorizationStatus::Authorized;
}
else if (authStatus == AVAuthorizationStatusNotDetermined)
{
return AuthorizationStatus::NotDetermined;
}
return AuthorizationStatus::Denied;
}
else
{
return AuthorizationStatus::Authorized;
}
}
void CMacOSMicrophoneAccess::requestAccess()
{
if (@available(macOS 10.14, *))
{
NSString *mediaType = AVMediaTypeAudio;
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler: ^ (BOOL granted)
{
emit permissionRequestAnswered(granted);
}];
}
else
{
emit permissionRequestAnswered(true);
}
}