Skip to content

Commit

Permalink
Split workarounds to fix audio on some devices
Browse files Browse the repository at this point in the history
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 <#3801>
  • Loading branch information
rom1v committed Mar 14, 2023
1 parent 337d6c2 commit 3582592
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 11 deletions.
12 changes: 6 additions & 6 deletions server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -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:
// - <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<>();
Expand Down
31 changes: 26 additions & 5 deletions server/src/main/java/com/genymobile/scrcpy/Workarounds.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -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());
}
}
}

0 comments on commit 3582592

Please sign in to comment.