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 <https://github.com/Genymobile/scrcpy/issues/3801>
This commit is contained in:
Romain Vimont 2023-03-14 22:51:31 +01:00
parent d2b7315ba6
commit 6ba99a62ff
2 changed files with 32 additions and 11 deletions

View file

@ -81,15 +81,15 @@ public final class Server {
// But only apply when strictly necessary, since workarounds can cause other issues:
// - <https://github.com/Genymobile/scrcpy/issues/940>
// - <https://github.com/Genymobile/scrcpy/issues/994>
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<AsyncProcessor> asyncProcessors = new ArrayList<>();

View file

@ -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());
}
}
}