diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java index 738203dedc..6501d4cf26 100644 --- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java +++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java @@ -2,11 +2,11 @@ import android.annotation.TargetApi; import android.content.AttributionSource; -import android.content.ContextWrapper; +import android.content.MutableContextWrapper; import android.os.Build; import android.os.Process; -public final class FakeContext extends ContextWrapper { +public final class FakeContext extends MutableContextWrapper { public static final String PACKAGE_NAME = "com.android.shell"; public static final int ROOT_UID = 0; // Like android.os.Process.ROOT_UID, but before API 29 diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java index ded1d9cb34..e0b312749a 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java +++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java @@ -4,6 +4,7 @@ import android.annotation.TargetApi; import android.app.Application; import android.content.AttributionSource; +import android.content.Context; import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; import android.media.AudioAttributes; @@ -30,22 +31,45 @@ private Workarounds() { public static void apply(boolean audio) { Workarounds.prepareMainLooper(); - // Workarounds must be applied for Meizu phones: - // - - // - - // - - // - // But only apply when strictly necessary, since workarounds can cause other issues: - // - - // - + boolean mustFillAppInfo = false; + boolean mustFillBaseContext = false; + boolean mustFillAppContext = false; + + if (Build.BRAND.equalsIgnoreCase("meizu")) { - Workarounds.fillAppInfo(); + // Workarounds must be applied for Meizu phones: + // - + // - + // - + // + // But only apply when strictly necessary, since workarounds can cause other issues: + // - + // - + mustFillAppInfo = true; + } else if (Build.BRAND.equalsIgnoreCase("honor")) { + // Honor devices require in addition a system context as a base context of FakeContext: + // - + // The system context must not be set for all devices, because it would cause other problems: + // - + // - + mustFillBaseContext = true; + mustFillAppContext = true; } - // 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 the application context for the AudioRecord to work. if (audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { + // 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 the application context for the AudioRecord to work. + mustFillAppContext = true; + } + + if (mustFillAppInfo) { + Workarounds.fillAppInfo(); + } + if (mustFillBaseContext) { + Workarounds.fillBaseContext(); + } + if (mustFillAppContext) { Workarounds.fillAppContext(); } } @@ -128,6 +152,19 @@ private static void fillAppContext() { } } + public static void fillBaseContext() { + try { + fillActivityThread(); + + Method getSystemContextMethod = activityThreadClass.getDeclaredMethod("getSystemContext"); + Context context = (Context) getSystemContextMethod.invoke(activityThread); + FakeContext.get().setBaseContext(context); + } catch (Throwable throwable) { + // this is a workaround, so failing is not an error + Ln.d("Could not fill base context: " + throwable.getMessage()); + } + } + @TargetApi(Build.VERSION_CODES.R) @SuppressLint("WrongConstant,MissingPermission,BlockedPrivateApi,SoonBlockedPrivateApi,DiscouragedPrivateApi") public static AudioRecord createAudioRecord(int source, int sampleRate, int channelConfig, int channels, int channelMask, int encoding) {