mirror of
https://github.com/Genymobile/scrcpy
synced 2024-12-18 13:24:40 +00:00
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:
parent
337d6c2fd3
commit
3582592d2c
@ -81,15 +81,15 @@ public final class Server {
|
|||||||
// But only apply when strictly necessary, since workarounds can cause other issues:
|
// 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/940>
|
||||||
// - <https://github.com/Genymobile/scrcpy/issues/994>
|
// - <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.
|
// Before Android 11, audio is not supported.
|
||||||
// Since Android 12, we can properly set a context on the AudioRecord.
|
// 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.
|
// Only on Android 11 we must fill the application context for the AudioRecord to work.
|
||||||
mustFillAppInfo |= audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R;
|
if (audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
|
||||||
|
Workarounds.fillAppContext();
|
||||||
if (mustFillAppInfo) {
|
|
||||||
Workarounds.fillAppInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AsyncProcessor> asyncProcessors = new ArrayList<>();
|
List<AsyncProcessor> asyncProcessors = new ArrayList<>();
|
||||||
|
@ -10,6 +10,10 @@ import java.lang.reflect.Constructor;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
public final class Workarounds {
|
public final class Workarounds {
|
||||||
|
|
||||||
|
private static Class<?> activityThreadClass;
|
||||||
|
private static Object activityThread;
|
||||||
|
|
||||||
private Workarounds() {
|
private Workarounds() {
|
||||||
// not instantiable
|
// not instantiable
|
||||||
}
|
}
|
||||||
@ -28,18 +32,25 @@ public final class Workarounds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||||
public static void fillAppInfo() {
|
private static void fillActivityThread() throws Exception {
|
||||||
try {
|
if (activityThread == null) {
|
||||||
// ActivityThread activityThread = new ActivityThread();
|
// ActivityThread activityThread = new ActivityThread();
|
||||||
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
|
activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||||
Constructor<?> activityThreadConstructor = activityThreadClass.getDeclaredConstructor();
|
Constructor<?> activityThreadConstructor = activityThreadClass.getDeclaredConstructor();
|
||||||
activityThreadConstructor.setAccessible(true);
|
activityThreadConstructor.setAccessible(true);
|
||||||
Object activityThread = activityThreadConstructor.newInstance();
|
activityThread = activityThreadConstructor.newInstance();
|
||||||
|
|
||||||
// ActivityThread.sCurrentActivityThread = activityThread;
|
// ActivityThread.sCurrentActivityThread = activityThread;
|
||||||
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
|
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
|
||||||
sCurrentActivityThreadField.setAccessible(true);
|
sCurrentActivityThreadField.setAccessible(true);
|
||||||
sCurrentActivityThreadField.set(null, activityThread);
|
sCurrentActivityThreadField.set(null, activityThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||||
|
public static void fillAppInfo() {
|
||||||
|
try {
|
||||||
|
fillActivityThread();
|
||||||
|
|
||||||
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
|
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
|
||||||
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
|
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
|
||||||
@ -59,6 +70,16 @@ public final class Workarounds {
|
|||||||
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||||
mBoundApplicationField.setAccessible(true);
|
mBoundApplicationField.setAccessible(true);
|
||||||
mBoundApplicationField.set(activityThread, appBindData);
|
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();
|
Application app = Application.class.newInstance();
|
||||||
Field baseField = ContextWrapper.class.getDeclaredField("mBase");
|
Field baseField = ContextWrapper.class.getDeclaredField("mBase");
|
||||||
@ -71,7 +92,7 @@ public final class Workarounds {
|
|||||||
mInitialApplicationField.set(activityThread, app);
|
mInitialApplicationField.set(activityThread, app);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
// this is a workaround, so failing is not an error
|
// 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user