让wayland合成器接管android合成器

2,032 阅读7分钟

原文

某天在翻看linux新闻时,意外了解到几年前,Android也使用现在GNU/Linux普遍使用的DRM(Direct Rendering Manager)显示架构

image.png

Android容器内的drm_info

所以,理论上可以让Wayland合成器接替Android合成器,让wayland直接显示输出,减少termux-x11这些中转产生的性能损耗。

尝试在chroot内直接运行kwin_wayland合成器,以及基于wlroots的labwc,均以失败告终:

image.png

kwin_wayland

image.png

wlroots的seat后端提示

根据seat的提示,可以推测是Android的合成器占用了drm,从而wlroots等合成器无法运行。

临近考试,这件事也被搁置了很久


既然被占用,杀掉不就可以了吗?

连接电脑,adb shell

diting:/ # lsof /dev/dri/card0                                                                                   
COMMAND     PID       USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
composer-servic  4328     system    9u      CHR              226,0       0t0       1448 /dev/dri/card0
composer-servic  4328     system   17u      CHR              226,0       0t0       1448 /dev/dri/card0
composer-servic  4328     system   20u      CHR              226,0       0t0       1448 /dev/dri/card0
......

再开个窗口

使用termux的ssh二进制文件连接容器内sshd

diting:/ # /data/data/com.termux/files/usr/bin/ssh SnowNF@127.0.0.1
SnowNF@127.0.0.1's password: 
SnowNF@localhost:~$ 

写两个脚本方便后续执行

SnowNF@localhost:~$ cat ./run.sh
sh chmod.sh&
export SEATD_VTBOUND=0
sudo -E seatd

SnowNF@localhost:~$ cat ./chmod.sh
sleep 1
sudo chmod 777 /run/seatd.sock

然后运行seatd

SnowNF@localhost:~$ ./run.sh 
00:00:00.000  [seatd/seat.c:41] Created seat seat0
00:00:00.000  [seatd/seatd.c:194] seatd started  

再开一个,设置环境变量使用cpu渲染[1],随后启动labwc

SnowNF@localhost:~$ export WLR_RENDERER=pixman
SnowNF@localhost:~$ labwc -d

此时可以在seatd看到drm设备被占用的信息

00:01:11.336  [seatd/server.c:145] New client connected (pid: 21205, uid: 1000, gid: 60000)
00:01:11.336  [seatd/seat.c:170] Added client 2 to seat0
00:01:11.336  [seatd/seat.c:480] Opened client 2 on seat0
00:01:11.340  [seatd/seat.c:281] Could not make device fd drm master: Device or resource busy
00:01:11.344  [seatd/client.c:471] Client disconnected
00:01:11.344  [seatd/seat.c:332] Could not revoke drm master on device fd: Invalid argument
00:01:11.344  [seatd/seat.c:418] No clients on seat0 to activate
00:01:11.344  [seatd/seat.c:524] Closed client 2 on seat0
00:01:11.344  [seatd/seat.c:192] Removed client 2 from seat0 

以及labwc失败的信息

00:00:00.001 [backend/session/session.c:109] Successfully loaded libseat session
00:00:00.004 [backend/backend.c:220] Found 1 GPUs
00:00:00.005 [backend/drm/backend.c:200] Initializing DRM backend for /dev/dri/card0 (msm_drm)
00:00:00.005 [backend/drm/drm.c:88] Using atomic DRM interface
00:00:00.005 [backend/drm/drm.c:100] ADDFB2 modifiers supported
00:00:00.005 [backend/drm/drm.c:253] Found 2 DRM CRTCs
00:00:00.005 [backend/drm/drm.c:180] Found 16 DRM planes
00:00:00.006 [util/env.c:25] Loading WLR_RENDERER option: pixman
00:00:00.006 [render/pixman/renderer.c:515] Creating pixman renderer
00:00:00.006 [render/allocator/allocator.c:143] Failed to create allocator
00:00:00.006 [../src/server.c:276] unable to create allocator  

再开一个,得到pid为4328,先记住,后面要用

$ adb shell
diting:/ $ su
diting:/ # lsof /dev/dri/card0
COMMAND     PID       USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
composer-servic  4328     system    9u      CHR              226,0       0t0       1448 /dev/dri/card0
diting:/ # stop # 停止安卓服务防止被快速拉起

把之前labwc的窗口稍作修改,4328要替换为自己的

SnowNF@localhost:~$ sudo kill 4328 && labwc -d

不出意外会因为缺少输入设备而失败

image.png

libinput initialization failed, no input devices

可以暂时用 ```shell SnowNF@localhost:~$ export WLR_LIBINPUT_NO_DEVICES=1 ``` 环境变量顶替一下

可能运行成功,因为 labwc 默认就是一张纯黑的壁纸,手机是OLED屏,无法判断是否真的"亮起"

此时可以临时换成 sway -d

然后就可以看见经典的 sway 标志了

image.png 图片的左上角是sway的标题栏,可见手机的DPI还是很高的 也能看见容器内进程正在使用 /dev/dri/card0

diting:/ # lsof /dev/dri/card0                                                                                                                                                                
COMMAND     PID       USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
seatd     20218       root    7u      CHR              226,0       0t0       1448 /data/data/com.termux/files/home/debian/dev/dri/card0
sway      20815     system   10u      CHR              226,0       0t0       1448 /data/data/com.termux/files/home/debian/dev/dri/card0
Xwayland  20835     system    4u      CHR              226,0       0t0       1448 /data/data/com.termux/files/home/debian/dev/dri/card0
composer-servic 20246     system    9u      CHR              226,0       0t0       1448 /dev/dri/card0
composer-servic 20246     system   17u      CHR              226,0       0t0       1448 /dev/dri/card0

没有输入只能看着...(还不如不搞)

SnowNF@localhost:~$ libinput list-devices

返回为空

搜索&翻看源码可知是调用了systemd的udev

网上都是执行

sudo udevadm trigger

来让systemd刷新一下设备列表

但容器内会提示

SnowNF@localhost:~$ sudo udevadm trigger
Running in chroot, ignoring request.

怎么办呢,只能看源码然后想办法去掉判断了

image.png

意外找到的注释

根据注释只需
SnowNF@localhost:~$ SYSTEMD_IGNORE_CHROOT=1 sudo -E udevadm trigger

随后我的手机重启了

估计是把所有硬件都刷新了一遍,导致安卓误以为故障导致重启

那有什么办法只“刷新”自己想要的吗

evtest 真的救我老命了(

SnowNF@localhost:~$ evtest 
No device specified, trying to scan all of /dev/input/event*
Not running as root, no devices may be available.
Available devices:
/dev/input/event0:      gpio-keys
/dev/input/event1:      qcom-hv-haptics
/dev/input/event2:      uinput-goodix
/dev/input/event3:      pmic_pwrkey
/dev/input/event4:      pmic_resin
/dev/input/event5:      pmic_pwrkey_bark
/dev/input/event6:      pmic_pwrkey_resin_bark
/dev/input/event7:      goodix_ts
/dev/input/event8:      waipio-mtp-snd-card Headset Jack
/dev/input/event9:      waipio-mtp-snd-card Button Jack
Select the device event number [0-9]:  

可见触摸等各种设备,这边就只弄个触摸就行了,即 /dev/input/event7

SnowNF@localhost:~$ sudo udevadm test /dev/input/event7
This program is for debugging only, it does not run any program
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.

Trying to open "/etc/systemd/hwdb/hwdb.bin"...
Trying to open "/etc/udev/hwdb.bin"...
Trying to open "/usr/lib/systemd/hwdb/hwdb.bin"...
Trying to open "/usr/lib/udev/hwdb.bin"...
.....
DEVPATH=/devices/virtual/input/input7/event7
DEVNAME=/dev/input/event7
MAJOR=13
MINOR=71
SUBSYSTEM=input
ACTION=add
TAGS=:power-switch:
ID_INPUT=1
ID_INPUT_TOUCHSCREEN=1
ID_INPUT_KEY=1
CURRENT_TAGS=:power-switch:
LIBINPUT_DEVICE_GROUP=0/beef/dead:goodix_ts
USEC_INITIALIZED=17731339063
run: 'libinput-fuzz-to-zero /sys/devices/virtual/input/input7/event7'
Unload kernel module index.
Unloaded link configuration context.

然后就注册完成了

SnowNF@localhost:~$ libinput list-devices
Failed to open /dev/input/event7 (Permission denied)  # 不知道为什么systemd把权限搞坏了
SnowNF@localhost:~$ sudo chmod 777 /dev/input/event7
SnowNF@localhost:~$ libinput list-devices
Device:           goodix_ts
Kernel:           /dev/input/event7
Group:            1
Seat:             seat0, default
Capabilities:     keyboard touch 
Tap-to-click:     n/a
Tap-and-drag:     n/a
Tap drag lock:    n/a
Left-handed:      n/a
Nat.scrolling:    n/a
Middle emulation: n/a
Calibration:      identity matrix
Scroll methods:   none
Click methods:    none
Disable-w-typing: n/a
Disable-w-trackpointing: n/a
Accel profiles:   n/a
Rotation:         0.0

触摸也有反应了

SnowNF@localhost:~$ libinput debug-events
-event7   DEVICE_ADDED            goodix_ts                         seat0 default group1  cap:kt ntouches 10 calib
 event7   TOUCH_DOWN              +0.000s       0 (0) 55.67/65.18 (6792.00/17678.00mm)
 event7   TOUCH_FRAME             +0.000s
 event7   TOUCH_UP                +0.043s       0 (0)
 event7   TOUCH_FRAME             +0.043s
^C

使用同样的方法注册了蓝牙鼠标

不会配置 labwc/sway 的触摸就用终端简单开了一些程序[2]

image.png

wlroots 和 android 内核

image.png

打开的终端以及vkcube (重影是因为平板的相机不是很好)

运行完毕后,Ctrl+C 结束 seatd 以及 labwc,随后在 adb shell 内执行以下命令

diting:/ # lsof /dev/dri/card0                                                                                   
COMMAND     PID       USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
composer-servic 15890     system    9u      CHR              226,0       0t0       1448 /dev/dri/card0
composer-servic 15890     system   17u      CHR              226,0       0t0       1448 /dev/dri/card0
.....
diting:/ # start
diting:/ # kill 15890 # 杀掉无法正常显示的android合成器,随后会自动拉起

最后手机正常开机


FAQ:

Q: 为什么要使用cpu渲染?

A: 因为如果使用 gles2 渲染, 需要 msm_drm 驱动来把渲染直接输出到DRM层,而现在mesa没有提供只能使用cpu渲染。更新: 修改 mesa 后实现了 msm-drm 驱动,成功在高通设备上实现了部分硬件加速

image.png

使用 WLR_RENDERER=gles2 渲染的报错

Q: 有什么可以改进的地方吗?

A: 这些操作大概可以封装成几个脚本,最后Vulkan可以使用turnip驱动,OpenGL可以使用zink来实现Wayland合成器以外的全局硬件加速,同时systemd的注册设备应该可以写一个循环执行的脚本,对硬件变更实时响应。

Q: 有声音吗?

A: 没有,理论上可以在adb start后,在安卓端跑个pulseaudio桥接到容器内部.........或者用udevadm刷新音频设备,再由容器内接管?(比如alsa)(嘛....对音频架构这块不是很清楚..期待大佬们补充)。

Q: 使用场景&展望

A: 成熟后应该是可以把手机当做纯GNU/Linux来使用

Q: 性能怎么样?

A: 可能比容器内运行vncserver或者使用termux-x11要高,如果有 msm_drm 驱动,那么应该和手机直接运行主线Linux性能一致(比如postmarketos)。

总之部分systemd功能可以使用,使用 udevadm 刷新设备后基本都能在容器里的/run/udev/data上看见对应的节点。(也许,可能,大概)外设只是缺少容器内用户空间的驱动..........所以上限取决于大家的找(写)驱动的能力以及脑洞。

[1]详见 WLR_RENDERER

[2]第一次运行vkcube的时候屏幕上看不见窗体,估计是labwc等合成器默认把手机的DP输出当做主屏幕了,所以我接了鼠标把光标移动到副屏(即手机的主屏幕),这时vkcube窗体被放置到了副屏,我才可以看见。