Fix turn screen off on Android 14
On Android 14, the methods to access the display have been moved to DisplayControl, which is not in the core framework. Use a specific ClassLoader to access this class and its native dependencies. Fixes #3927 <https://github.com/Genymobile/scrcpy/issues/3927> Refs #3927 comment <https://github.com/Genymobile/scrcpy/issues/3927#issuecomment-1790031953> Refs #4446 comment <https://github.com/Genymobile/scrcpy/pull/4446#issuecomment-1824660915> PR #4456 <https://github.com/Genymobile/scrcpy/pull/4456> Co-authored-by: Simon Chan <1330321+yume-chan@users.noreply.github.com> Signed-off-by: Romain Vimont <rom@rom1v.com>
This commit is contained in:
parent
eed06b141a
commit
5d4b8a7e6d
@ -1,6 +1,7 @@
|
|||||||
package com.genymobile.scrcpy;
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
||||||
|
import com.genymobile.scrcpy.wrappers.DisplayControl;
|
||||||
import com.genymobile.scrcpy.wrappers.InputManager;
|
import com.genymobile.scrcpy.wrappers.InputManager;
|
||||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||||
@ -315,8 +316,12 @@ public final class Device {
|
|||||||
*/
|
*/
|
||||||
public static boolean setScreenPowerMode(int mode) {
|
public static boolean setScreenPowerMode(int mode) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
// On Android 14, these internal methods have been moved to DisplayControl
|
||||||
|
boolean useDisplayControl =
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && !SurfaceControl.hasPhysicalDisplayIdsMethod();
|
||||||
|
|
||||||
// Change the power mode for all physical displays
|
// Change the power mode for all physical displays
|
||||||
long[] physicalDisplayIds = SurfaceControl.getPhysicalDisplayIds();
|
long[] physicalDisplayIds = useDisplayControl ? DisplayControl.getPhysicalDisplayIds() : SurfaceControl.getPhysicalDisplayIds();
|
||||||
if (physicalDisplayIds == null) {
|
if (physicalDisplayIds == null) {
|
||||||
Ln.e("Could not get physical display ids");
|
Ln.e("Could not get physical display ids");
|
||||||
return false;
|
return false;
|
||||||
@ -324,7 +329,8 @@ public final class Device {
|
|||||||
|
|
||||||
boolean allOk = true;
|
boolean allOk = true;
|
||||||
for (long physicalDisplayId : physicalDisplayIds) {
|
for (long physicalDisplayId : physicalDisplayIds) {
|
||||||
IBinder binder = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
|
IBinder binder = useDisplayControl ? DisplayControl.getPhysicalDisplayToken(
|
||||||
|
physicalDisplayId) : SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
|
||||||
allOk &= SurfaceControl.setDisplayPowerMode(binder, mode);
|
allOk &= SurfaceControl.setDisplayPowerMode(binder, mode);
|
||||||
}
|
}
|
||||||
return allOk;
|
return allOk;
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.genymobile.scrcpy.wrappers;
|
||||||
|
|
||||||
|
import com.genymobile.scrcpy.Ln;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
@SuppressLint({"PrivateApi", "SoonBlockedPrivateApi", "BlockedPrivateApi"})
|
||||||
|
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
public final class DisplayControl {
|
||||||
|
|
||||||
|
private static final Class<?> CLASS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Class<?> displayControlClass = null;
|
||||||
|
try {
|
||||||
|
Class<?> classLoaderFactoryClass = Class.forName("com.android.internal.os.ClassLoaderFactory");
|
||||||
|
Method createClassLoaderMethod = classLoaderFactoryClass.getDeclaredMethod("createClassLoader", String.class, String.class, String.class,
|
||||||
|
ClassLoader.class, int.class, boolean.class, String.class);
|
||||||
|
ClassLoader classLoader = (ClassLoader) createClassLoaderMethod.invoke(null, "/system/framework/services.jar", null, null,
|
||||||
|
ClassLoader.getSystemClassLoader(), 0, true, null);
|
||||||
|
|
||||||
|
displayControlClass = classLoader.loadClass("com.android.server.display.DisplayControl");
|
||||||
|
|
||||||
|
Method loadMethod = Runtime.class.getDeclaredMethod("loadLibrary0", Class.class, String.class);
|
||||||
|
loadMethod.setAccessible(true);
|
||||||
|
loadMethod.invoke(Runtime.getRuntime(), displayControlClass, "android_servers");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Ln.e("Could not initialize DisplayControl", e);
|
||||||
|
// Do not throw an exception here, the methods will fail when they are called
|
||||||
|
}
|
||||||
|
CLASS = displayControlClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Method getPhysicalDisplayTokenMethod;
|
||||||
|
private static Method getPhysicalDisplayIdsMethod;
|
||||||
|
|
||||||
|
private DisplayControl() {
|
||||||
|
// only static methods
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Method getGetPhysicalDisplayTokenMethod() throws NoSuchMethodException {
|
||||||
|
if (getPhysicalDisplayTokenMethod == null) {
|
||||||
|
getPhysicalDisplayTokenMethod = CLASS.getMethod("getPhysicalDisplayToken", long.class);
|
||||||
|
}
|
||||||
|
return getPhysicalDisplayTokenMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
|
||||||
|
try {
|
||||||
|
Method method = getGetPhysicalDisplayTokenMethod();
|
||||||
|
return (IBinder) method.invoke(null, physicalDisplayId);
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Method getGetPhysicalDisplayIdsMethod() throws NoSuchMethodException {
|
||||||
|
if (getPhysicalDisplayIdsMethod == null) {
|
||||||
|
getPhysicalDisplayIdsMethod = CLASS.getMethod("getPhysicalDisplayIds");
|
||||||
|
}
|
||||||
|
return getPhysicalDisplayIdsMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long[] getPhysicalDisplayIds() {
|
||||||
|
try {
|
||||||
|
Method method = getGetPhysicalDisplayIdsMethod();
|
||||||
|
return (long[]) method.invoke(null);
|
||||||
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -139,6 +139,15 @@ public final class SurfaceControl {
|
|||||||
return getPhysicalDisplayIdsMethod;
|
return getPhysicalDisplayIdsMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasPhysicalDisplayIdsMethod() {
|
||||||
|
try {
|
||||||
|
getGetPhysicalDisplayIdsMethod();
|
||||||
|
return true;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static long[] getPhysicalDisplayIds() {
|
public static long[] getPhysicalDisplayIds() {
|
||||||
try {
|
try {
|
||||||
Method method = getGetPhysicalDisplayIdsMethod();
|
Method method = getGetPhysicalDisplayIdsMethod();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user