Skip to content

Commit

Permalink
Add workarounds for Honor devices
Browse files Browse the repository at this point in the history
Audio did not work on Honor devices.

Two workarounds are necessary:
 - a system context must be set as a base context of FakeContext (so
   that a PackageManager is available),
 - the same workaround as for Meizu phones must also be applied (so that
   ActivityThread.currentApplication() return a valid instance).

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

Fixes #4015 <#4015>
Refs #3085 <#3805>

Co-authored-by: Simon Chan <1330321+yume-chan@users.noreply.github.com>
  • Loading branch information
rom1v and yume-chan committed Jun 18, 2023
1 parent 48a00fb commit 86e5c90
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 11 deletions.
4 changes: 2 additions & 2 deletions server/src/main/java/com/genymobile/scrcpy/FakeContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 16 additions & 8 deletions server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,23 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc

Workarounds.prepareMainLooper();

// Workarounds must be applied for Meizu phones:
// - <https://github.com/Genymobile/scrcpy/issues/240>
// - <https://github.com/Genymobile/scrcpy/issues/365>
// - <https://github.com/Genymobile/scrcpy/issues/2656>
//
// 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>
if (Build.BRAND.equalsIgnoreCase("meizu")) {
// Workarounds must be applied for Meizu phones:
// - <https://github.com/Genymobile/scrcpy/issues/240>
// - <https://github.com/Genymobile/scrcpy/issues/365>
// - <https://github.com/Genymobile/scrcpy/issues/2656>
//
// 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>
Workarounds.fillAppInfo();
} else if (Build.BRAND.equalsIgnoreCase("honor")) {
// Honor devices require both a system context (as a base context of FakeContext) and the same workarounds as for Meizu phones:
// - <https://github.com/Genymobile/scrcpy/issues/4015>
// The system context must not be set for all devices, it would cause other problems:
// - <https://github.com/Genymobile/scrcpy/issues/4015#issuecomment-1595382142>
// - <https://github.com/Genymobile/scrcpy/issues/3805#issuecomment-1596148031>
Workarounds.fillBaseContext();
Workarounds.fillAppInfo();
}

Expand Down
25 changes: 24 additions & 1 deletion server/src/main/java/com/genymobile/scrcpy/Workarounds.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -22,6 +23,7 @@ public final class Workarounds {

private static Class<?> activityThreadClass;
private static Object activityThread;
private static boolean currentActivityThreadInitialized;

private Workarounds() {
// not instantiable
Expand All @@ -41,18 +43,26 @@ public static void prepareMainLooper() {
}

@SuppressLint("PrivateApi,DiscouragedPrivateApi")
private static void fillActivityThread() throws Exception {
private static void initActivityThread() throws Exception {
if (activityThread == null) {
// ActivityThread activityThread = new ActivityThread();
activityThreadClass = Class.forName("android.app.ActivityThread");
Constructor<?> activityThreadConstructor = activityThreadClass.getDeclaredConstructor();
activityThreadConstructor.setAccessible(true);
activityThread = activityThreadConstructor.newInstance();
}
}

@SuppressLint("PrivateApi,DiscouragedPrivateApi")
private static void fillActivityThread() throws Exception {
initActivityThread();

if (!currentActivityThreadInitialized) {
// ActivityThread.sCurrentActivityThread = activityThread;
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true);
sCurrentActivityThreadField.set(null, activityThread);
currentActivityThreadInitialized = true;
}
}

Expand Down Expand Up @@ -105,6 +115,19 @@ public static void fillAppContext() {
}
}

public static void fillBaseContext() {
try {
initActivityThread();

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) {
Expand Down

0 comments on commit 86e5c90

Please sign in to comment.