From 3582592d2c50304146338606678ea58f05a7674f 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 5800487da9..244913cf1b 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 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc // 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 64cc127232..89380ece4a 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.Field; public final class Workarounds { + + private static Class activityThreadClass; + private static Object activityThread; + private Workarounds() { // not instantiable } @@ -28,18 +32,25 @@ public static void prepareMainLooper() { } @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 static void fillAppInfo() { 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 static void fillAppInfo() { 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()); } } }