From 2e8e9016696ce2a6e78423c875da353d5960fe43 Mon Sep 17 00:00:00 2001 From: Ilya Arkhanhelsky Date: Mon, 23 Dec 2024 14:37:25 +0100 Subject: [PATCH] fix: update scrcpy server to support Android 15 --- server/.java-version | 1 + server/build.gradle | 6 ++-- .../java/com/genymobile/scrcpy/Device.java | 7 ++++ .../com/genymobile/scrcpy/ScreenEncoder.java | 32 ++++++++++++++++--- .../scrcpy/wrappers/DisplayManager.java | 17 ++++++++++ .../scrcpy/wrappers/SurfaceControl.java | 8 ++--- 6 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 server/.java-version diff --git a/server/.java-version b/server/.java-version new file mode 100644 index 0000000000..bec3a35ee8 --- /dev/null +++ b/server/.java-version @@ -0,0 +1 @@ +system diff --git a/server/build.gradle b/server/build.gradle index 4dbeaf142e..f4fb32fdd5 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 30 + compileSdkVersion 35 defaultConfig { applicationId "com.genymobile.scrcpy" minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 35 versionCode 11900 - versionName "1.19-ws5" + versionName "1.19-ws6" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index 49bae00733..c427cd041f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -29,6 +29,9 @@ public final class Device { public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2; private static final ServiceManager SERVICE_MANAGER = new ServiceManager(); + public static ServiceManager getServiceManager() { + return SERVICE_MANAGER; + } public interface RotationListener { void onRotationChanged(int rotation); @@ -136,6 +139,10 @@ public void applyNewVideoSetting(VideoSettings videoSettings) { this.setScreenInfo(ScreenInfo.computeScreenInfo(Device.getDisplayInfo(displayId), videoSettings)); } + public int getDisplayId() { + return displayId; + } + public synchronized void setScreenInfo(ScreenInfo screenInfo) { this.screenInfo = screenInfo; } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index 53514e7b11..7624d648e8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -17,6 +17,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import android.hardware.display.VirtualDisplay; public class ScreenEncoder implements Connection.StreamInvalidateListener, Runnable { @@ -94,10 +95,13 @@ private void internalStreamScreen() throws IOException { updateFormat(); connection.setStreamInvalidateListener(this); boolean alive; + IBinder display = null; + VirtualDisplay virtualDisplay = null; + try { do { MediaCodec codec = createCodec(videoSettings.getEncoderName()); - IBinder display = createDisplay(); + ScreenInfo screenInfo = device.getScreenInfo(); Rect contentRect = screenInfo.getContentRect(); // include the locked video orientation @@ -110,14 +114,34 @@ private void internalStreamScreen() throws IOException { setSize(format, videoRect.width(), videoRect.height()); configure(codec, format); Surface surface = codec.createInputSurface(); - setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack); + + try { + display = createDisplay(); + setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack); + } catch (Exception e) { + try { + virtualDisplay = Device.getServiceManager().getDisplayManager() + .createVirtualDisplay("scrcpy", videoRect.width(), videoRect.height(), device.getDisplayId(), surface); + } catch(Exception x) { + + } + + } codec.start(); try { alive = encode(codec); // do not call stop() on exception, it would trigger an IllegalStateException codec.stop(); } finally { - destroyDisplay(display); + if (display != null) { + destroyDisplay(display); + display = null; + } + if (virtualDisplay != null) { + virtualDisplay.release(); + virtualDisplay = null; + } + codec.release(); surface.release(); } @@ -250,7 +274,7 @@ private static MediaFormat createFormat(VideoSettings videoSettings) { return format; } - private static IBinder createDisplay() { + private static IBinder createDisplay() throws Exception { // Since Android 12 (preview), secure displays could not be created with shell permissions anymore. // On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S". boolean secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !"S" diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java index 3d3477095a..3f6f691de8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -3,14 +3,18 @@ import com.genymobile.scrcpy.DisplayInfo; import com.genymobile.scrcpy.Ln; import com.genymobile.scrcpy.Size; +import android.hardware.display.VirtualDisplay; + import android.os.IInterface; import android.view.Display; +import android.view.Surface; import java.lang.reflect.Method; public final class DisplayManager { private final IInterface manager; + private Method createVirtualDisplayMethod; public DisplayManager(IInterface manager) { this.manager = manager; @@ -49,4 +53,17 @@ public int[] getDisplayIds() { throw new AssertionError(e); } } + + private Method getCreateVirtualDisplayMethod() throws NoSuchMethodException { + if (createVirtualDisplayMethod == null) { + createVirtualDisplayMethod = android.hardware.display.DisplayManager.class + .getMethod("createVirtualDisplay", String.class, int.class, int.class, int.class, Surface.class); + } + return createVirtualDisplayMethod; + } + + public VirtualDisplay createVirtualDisplay(String name, int width, int height, int displayIdToMirror, Surface surface) throws Exception { + Method method = getCreateVirtualDisplayMethod(); + return (VirtualDisplay) method.invoke(null, name, width, height, displayIdToMirror, surface); + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java index 8fbb860b97..ac37390086 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java @@ -76,12 +76,8 @@ public static void setDisplaySurface(IBinder displayToken, Surface surface) { } } - public static IBinder createDisplay(String name, boolean secure) { - try { - return (IBinder) CLASS.getMethod("createDisplay", String.class, boolean.class).invoke(null, name, secure); - } catch (Exception e) { - throw new AssertionError(e); - } + public static IBinder createDisplay(String name, boolean secure) throws Exception { + return (IBinder) CLASS.getMethod("createDisplay", String.class, boolean.class).invoke(null, name, secure); } private static Method getGetBuiltInDisplayMethod() throws NoSuchMethodException {