From 3626d90004c9946320152564a375e56f9c5030f4 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 16 Mar 2023 22:19:21 +0100 Subject: [PATCH] Use separate audio capture code for Android 11 The code to start audio capture is more complicated for Android 11 (launch a fake popup, wait, make several attempts, close the shell package). Use a distinct code path specific to Android 11. --- .../com/genymobile/scrcpy/AudioCapture.java | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java index e15c8285..c940db16 100644 --- a/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/AudioCapture.java @@ -59,27 +59,21 @@ public final class AudioCapture { } private static void startWorkaroundAndroid11() { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { - // Android 11 requires Apps to be at foreground to record audio. - // Normally, each App has its own user ID, so Android checks whether the requesting App has the user ID that's at the foreground. - // But scrcpy server is NOT an App, it's a Java application started from Android shell, so it has the same user ID (2000) with Android - // shell ("com.android.shell"). - // If there is an Activity from Android shell running at foreground, then the permission system will believe scrcpy is also in the - // foreground. - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setComponent(new ComponentName(FakeContext.PACKAGE_NAME, "com.android.shell.HeapDumpActivity")); - ServiceManager.getActivityManager().startActivityAsUserWithFeature(intent); - } - } + // Android 11 requires Apps to be at foreground to record audio. + // Normally, each App has its own user ID, so Android checks whether the requesting App has the user ID that's at the foreground. + // But scrcpy server is NOT an App, it's a Java application started from Android shell, so it has the same user ID (2000) with Android + // shell ("com.android.shell"). + // If there is an Activity from Android shell running at foreground, then the permission system will believe scrcpy is also in the + // foreground. + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setComponent(new ComponentName(FakeContext.PACKAGE_NAME, "com.android.shell.HeapDumpActivity")); + ServiceManager.getActivityManager().startActivityAsUserWithFeature(intent); } private static void stopWorkaroundAndroid11() { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { - ServiceManager.getActivityManager().forceStopPackage(FakeContext.PACKAGE_NAME); - } + ServiceManager.getActivityManager().forceStopPackage(FakeContext.PACKAGE_NAME); } private void tryStartRecording(int attempts, int delayMs) throws AudioCaptureForegroundException { @@ -87,32 +81,36 @@ public final class AudioCapture { // Wait for activity to start SystemClock.sleep(delayMs); try { - recorder = createAudioRecord(); - recorder.startRecording(); + startRecording(); return; // it worked } catch (UnsupportedOperationException e) { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { - if (attempts == 0) { - Ln.e("Failed to start audio capture"); - Ln.e("On Android 11, audio capture must be started in the foreground, make sure that the device is unlocked when starting " - + "scrcpy."); - throw new AudioCaptureForegroundException(); - } else { - Ln.d("Failed to start audio capture, retrying..."); - } + if (attempts == 0) { + Ln.e("Failed to start audio capture"); + Ln.e("On Android 11, audio capture must be started in the foreground, make sure that the device is unlocked when starting " + + "scrcpy."); + throw new AudioCaptureForegroundException(); } else { - throw e; + Ln.d("Failed to start audio capture, retrying..."); } } } } + private void startRecording() { + recorder = createAudioRecord(); + recorder.startRecording(); + } + public void start() throws AudioCaptureForegroundException { - startWorkaroundAndroid11(); - try { - tryStartRecording(3, 100); - } finally { - stopWorkaroundAndroid11(); + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { + startWorkaroundAndroid11(); + try { + tryStartRecording(3, 100); + } finally { + stopWorkaroundAndroid11(); + } + } else { + startRecording(); } }