mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-02-22 14:24:43 +01:00
137 lines
5.8 KiB
Java
137 lines
5.8 KiB
Java
package com.genymobile.scrcpy.wrappers;
|
|
|
|
import com.genymobile.scrcpy.FakeContext;
|
|
import com.genymobile.scrcpy.device.DisplayInfo;
|
|
import com.genymobile.scrcpy.device.Size;
|
|
import com.genymobile.scrcpy.util.Command;
|
|
import com.genymobile.scrcpy.util.Ln;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.content.Context;
|
|
import android.hardware.display.VirtualDisplay;
|
|
import android.view.Display;
|
|
import android.view.Surface;
|
|
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Method;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
|
public final class DisplayManager {
|
|
private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal
|
|
private Method createVirtualDisplayMethod;
|
|
|
|
static DisplayManager create() {
|
|
try {
|
|
Class<?> clazz = Class.forName("android.hardware.display.DisplayManagerGlobal");
|
|
Method getInstanceMethod = clazz.getDeclaredMethod("getInstance");
|
|
Object dmg = getInstanceMethod.invoke(null);
|
|
return new DisplayManager(dmg);
|
|
} catch (ReflectiveOperationException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
|
|
private DisplayManager(Object manager) {
|
|
this.manager = manager;
|
|
}
|
|
|
|
// public to call it from unit tests
|
|
public static DisplayInfo parseDisplayInfo(String dumpsysDisplayOutput, int displayId) {
|
|
Pattern regex = Pattern.compile(
|
|
"^ mOverrideDisplayInfo=DisplayInfo\\{\".*?, displayId " + displayId + ".*?(, FLAG_.*)?, real ([0-9]+) x ([0-9]+).*?, "
|
|
+ "rotation ([0-9]+).*?, density ([0-9]+).*?, layerStack ([0-9]+)",
|
|
Pattern.MULTILINE);
|
|
Matcher m = regex.matcher(dumpsysDisplayOutput);
|
|
if (!m.find()) {
|
|
return null;
|
|
}
|
|
int flags = parseDisplayFlags(m.group(1));
|
|
int width = Integer.parseInt(m.group(2));
|
|
int height = Integer.parseInt(m.group(3));
|
|
int rotation = Integer.parseInt(m.group(4));
|
|
int density = Integer.parseInt(m.group(5));
|
|
int layerStack = Integer.parseInt(m.group(6));
|
|
|
|
return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, density);
|
|
}
|
|
|
|
private static DisplayInfo getDisplayInfoFromDumpsysDisplay(int displayId) {
|
|
try {
|
|
String dumpsysDisplayOutput = Command.execReadOutput("dumpsys", "display");
|
|
return parseDisplayInfo(dumpsysDisplayOutput, displayId);
|
|
} catch (Exception e) {
|
|
Ln.e("Could not get display info from \"dumpsys display\" output", e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static int parseDisplayFlags(String text) {
|
|
Pattern regex = Pattern.compile("FLAG_[A-Z_]+");
|
|
if (text == null) {
|
|
return 0;
|
|
}
|
|
|
|
int flags = 0;
|
|
Matcher m = regex.matcher(text);
|
|
while (m.find()) {
|
|
String flagString = m.group();
|
|
try {
|
|
Field filed = Display.class.getDeclaredField(flagString);
|
|
flags |= filed.getInt(null);
|
|
} catch (ReflectiveOperationException e) {
|
|
// Silently ignore, some flags reported by "dumpsys display" are @TestApi
|
|
}
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
public DisplayInfo getDisplayInfo(int displayId) {
|
|
try {
|
|
Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, displayId);
|
|
if (displayInfo == null) {
|
|
// fallback when displayInfo is null
|
|
return getDisplayInfoFromDumpsysDisplay(displayId);
|
|
}
|
|
Class<?> cls = displayInfo.getClass();
|
|
// width and height already take the rotation into account
|
|
int width = cls.getDeclaredField("logicalWidth").getInt(displayInfo);
|
|
int height = cls.getDeclaredField("logicalHeight").getInt(displayInfo);
|
|
int rotation = cls.getDeclaredField("rotation").getInt(displayInfo);
|
|
int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo);
|
|
int flags = cls.getDeclaredField("flags").getInt(displayInfo);
|
|
int logicalDensityDpi = cls.getDeclaredField("logicalDensityDpi").getInt(displayInfo);
|
|
return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags, logicalDensityDpi);
|
|
} catch (ReflectiveOperationException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
|
|
public int[] getDisplayIds() {
|
|
try {
|
|
return (int[]) manager.getClass().getMethod("getDisplayIds").invoke(manager);
|
|
} catch (ReflectiveOperationException e) {
|
|
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, int.class);
|
|
}
|
|
return createVirtualDisplayMethod;
|
|
}
|
|
|
|
public VirtualDisplay createVirtualDisplay(String name, int width, int height, int dpi, Surface surface, int flags) throws Exception {
|
|
//Method method = getCreateVirtualDisplayMethod();
|
|
Constructor<android.hardware.display.DisplayManager> ctor = android.hardware.display.DisplayManager.class.getDeclaredConstructor(Context.class);
|
|
ctor.setAccessible(true);
|
|
android.hardware.display.DisplayManager dm = ctor.newInstance(FakeContext.get());
|
|
return dm.createVirtualDisplay(name, width, height, dpi, surface, flags);
|
|
//return (VirtualDisplay) method.invoke(null, name, width, height, dpi, surface, flags);
|
|
}
|
|
}
|