Add workarounds for Honor devices

Audio did not work on Honor devices.

To make it work, a system context must be set as a base context of
FakeContext (so that a PackageManager is available), and a current
Application and ActivityThread must be set.

These workarounds must not be applied for all devices, because they
might cause other issues.

Fixes #4015 <https://github.com/Genymobile/scrcpy/issues/4015>
Refs #3085 <https://github.com/Genymobile/scrcpy/issues/3805>

Co-authored-by: Simon Chan <1330321+yume-chan@users.noreply.github.com>
This commit is contained in:
Romain Vimont 2023-06-18 17:52:43 +02:00
parent 0f1afff7a6
commit fb21bbf763
2 changed files with 53 additions and 14 deletions

View file

@ -2,11 +2,11 @@ package com.genymobile.scrcpy;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.AttributionSource; import android.content.AttributionSource;
import android.content.ContextWrapper; import android.content.MutableContextWrapper;
import android.os.Build; import android.os.Build;
import android.os.Process; 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 String PACKAGE_NAME = "com.android.shell";
public static final int ROOT_UID = 0; // Like android.os.Process.ROOT_UID, but before API 29 public static final int ROOT_UID = 0; // Like android.os.Process.ROOT_UID, but before API 29

View file

@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Application; import android.app.Application;
import android.content.AttributionSource; import android.content.AttributionSource;
import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.media.AudioAttributes; import android.media.AudioAttributes;
@ -30,6 +31,12 @@ public final class Workarounds {
public static void apply(boolean audio) { public static void apply(boolean audio) {
Workarounds.prepareMainLooper(); Workarounds.prepareMainLooper();
boolean mustFillAppInfo = false;
boolean mustFillBaseContext = false;
boolean mustFillAppContext = false;
if (Build.BRAND.equalsIgnoreCase("meizu")) {
// Workarounds must be applied for Meizu phones: // Workarounds must be applied for Meizu phones:
// - <https://github.com/Genymobile/scrcpy/issues/240> // - <https://github.com/Genymobile/scrcpy/issues/240>
// - <https://github.com/Genymobile/scrcpy/issues/365> // - <https://github.com/Genymobile/scrcpy/issues/365>
@ -38,14 +45,33 @@ public final class Workarounds {
// 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>
if (Build.BRAND.equalsIgnoreCase("meizu")) { mustFillAppInfo = true;
Workarounds.fillAppInfo(); } else if (Build.BRAND.equalsIgnoreCase("honor")) {
// More workarounds must be applied for Honor devices:
// - <https://github.com/Genymobile/scrcpy/issues/4015>
//
// The system context must not be set for all devices, because it would cause other problems:
// - <https://github.com/Genymobile/scrcpy/issues/4015#issuecomment-1595382142>
// - <https://github.com/Genymobile/scrcpy/issues/3805#issuecomment-1596148031>
mustFillAppInfo = true;
mustFillBaseContext = true;
mustFillAppContext = true;
} }
if (audio && Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
// 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 the application context for the AudioRecord to work. // 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) { mustFillAppContext = true;
}
if (mustFillAppInfo) {
Workarounds.fillAppInfo();
}
if (mustFillBaseContext) {
Workarounds.fillBaseContext();
}
if (mustFillAppContext) {
Workarounds.fillAppContext(); Workarounds.fillAppContext();
} }
} }
@ -128,6 +154,19 @@ public final class Workarounds {
} }
} }
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) @TargetApi(Build.VERSION_CODES.R)
@SuppressLint("WrongConstant,MissingPermission,BlockedPrivateApi,SoonBlockedPrivateApi,DiscouragedPrivateApi") @SuppressLint("WrongConstant,MissingPermission,BlockedPrivateApi,SoonBlockedPrivateApi,DiscouragedPrivateApi")
public static AudioRecord createAudioRecord(int source, int sampleRate, int channelConfig, int channels, int channelMask, int encoding) { public static AudioRecord createAudioRecord(int source, int sampleRate, int channelConfig, int channels, int channelMask, int encoding) {