From 6ba99a62ff51c0ce209db78a9ed17f1633170027 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 14 Mar 2023 22:51:31 +0100 Subject: [PATCH] Split workarounds to fix audio on some devices There were several workarounds applied in a single method. Some of them are specific to Meizu phones, but cause issues on other devices. Split the method to be able to only fill the app context for audio capture without applying the Meizu workarounds. Fixes #3801 --- .../java/com/genymobile/scrcpy/Server.java | 12 +++---- .../com/genymobile/scrcpy/Workarounds.java | 31 ++++++++++++++++--- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 5800487d..244913cf 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -81,15 +81,15 @@ public final class Server { // But only apply when strictly necessary, since workarounds can cause other issues: // - // - - boolean mustFillAppInfo = Build.BRAND.equalsIgnoreCase("meizu"); + if (Build.BRAND.equalsIgnoreCase("meizu")) { + Workarounds.fillAppInfo(); + } // Before Android 11, audio is not supported. // Since Android 12, we can properly set a context on the AudioRecord. - // Only on Android 11 we must fill app info for the AudioRecord to work. - mustFillAppInfo |= audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R; - - if (mustFillAppInfo) { - Workarounds.fillAppInfo(); + // Only on Android 11 we must fill the application context for the AudioRecord to work. + if (audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { + Workarounds.fillAppContext(); } List asyncProcessors = new ArrayList<>(); diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index 64cc1272..89380ece 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -10,6 +10,10 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; public final class Workarounds { + + private static Class activityThreadClass; + private static Object activityThread; + private Workarounds() { // not instantiable } @@ -28,18 +32,25 @@ public final class Workarounds { } @SuppressLint("PrivateApi,DiscouragedPrivateApi") - public static void fillAppInfo() { - try { + private static void fillActivityThread() throws Exception { + if (activityThread == null) { // ActivityThread activityThread = new ActivityThread(); - Class activityThreadClass = Class.forName("android.app.ActivityThread"); + activityThreadClass = Class.forName("android.app.ActivityThread"); Constructor activityThreadConstructor = activityThreadClass.getDeclaredConstructor(); activityThreadConstructor.setAccessible(true); - Object activityThread = activityThreadConstructor.newInstance(); + activityThread = activityThreadConstructor.newInstance(); // ActivityThread.sCurrentActivityThread = activityThread; Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); sCurrentActivityThreadField.setAccessible(true); sCurrentActivityThreadField.set(null, activityThread); + } + } + + @SuppressLint("PrivateApi,DiscouragedPrivateApi") + public static void fillAppInfo() { + try { + fillActivityThread(); // ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData(); Class appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData"); @@ -59,6 +70,16 @@ public final class Workarounds { Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); mBoundApplicationField.set(activityThread, appBindData); + } catch (Throwable throwable) { + // this is a workaround, so failing is not an error + Ln.d("Could not fill app info: " + throwable.getMessage()); + } + } + + @SuppressLint("PrivateApi,DiscouragedPrivateApi") + public static void fillAppContext() { + try { + fillActivityThread(); Application app = Application.class.newInstance(); Field baseField = ContextWrapper.class.getDeclaredField("mBase"); @@ -71,7 +92,7 @@ public final class Workarounds { mInitialApplicationField.set(activityThread, app); } catch (Throwable throwable) { // this is a workaround, so failing is not an error - Ln.d("Could not fill app info: " + throwable.getMessage()); + Ln.d("Could not fill app context: " + throwable.getMessage()); } } }