diff --git a/cmd/modavd-gui/rsrc_windows_amd64.syso b/cmd/modavd-gui/rsrc_windows_amd64.syso index e75d5a9..8d57828 100644 Binary files a/cmd/modavd-gui/rsrc_windows_amd64.syso and b/cmd/modavd-gui/rsrc_windows_amd64.syso differ diff --git a/internal/buildinfo/buildinfo.go b/internal/buildinfo/buildinfo.go index 0c3459d..c511d34 100644 --- a/internal/buildinfo/buildinfo.go +++ b/internal/buildinfo/buildinfo.go @@ -4,7 +4,7 @@ package buildinfo // Version of modavd itself, kept in the source rather than set with -ldflags -X: release builds must come out byte // for byte the same for the same source (Mirror 酱 ships per-file deltas, and a version stamped per package would // change the exe in every release). The ModAVD package version comes from the root at run time. -var Version = "0.11.0" +var Version = "0.12.0" // Protocol is the version of the machine-readable contract: the commands, the JSON written by `--json`, the exit codes // and the MCP tool schemas. Callers such as AUTO-MAS check it (`modavd version --json`) before relying on a field. diff --git a/internal/emu/clocksource.go b/internal/emu/clocksource.go index 83a23c4..653a4b7 100644 --- a/internal/emu/clocksource.go +++ b/internal/emu/clocksource.go @@ -29,9 +29,10 @@ import ( const ( // clocksourceScript prints the current clock source, the available ones and the guest's machine, separated by - // "---" lines: one fixed command line without variables, readable without root. - clocksourceScript = "cat /sys/devices/system/clocksource/clocksource0/current_clocksource; echo ---; " + - "cat /sys/devices/system/clocksource/clocksource0/available_clocksource; echo ---; uname -m" + // "---" lines: one fixed command line without variables. As root: on the Android 14 images SELinux does not let + // the adb shell user read this sysfs directory (every boot logged "Permission denied"). + clocksourceScript = "su 0 sh -c 'cat /sys/devices/system/clocksource/clocksource0/current_clocksource; echo ---; " + + "cat /sys/devices/system/clocksource/clocksource0/available_clocksource; echo ---; uname -m'" clocksourceTimeout = 10 * time.Second // clocksourceKernelCommand is the guest kernel log as root (logcat -b kernel needs it on these images; dmesg may // have been overwritten by then). Filtered here, on the host, so no pipe runs in the guest. @@ -134,6 +135,7 @@ func (m *Manager) checkClocksource(i int, cfg *avd.Ini, run func(command string, rec.Available, rec.Arch, rec.X86 = available, arch, x86Guest(arch, cfg) switch { case !ok: + rec.Available = "" // whatever stood there is part of an error text rec.Error = orText(tail(strings.Join(strings.Fields(res.Out), " "), 200), fmt.Sprintf("退出码 %d", res.Code)) m.Log.Warnf("实例 %d 读不到客体时钟源(%s),这次开机不判断", i, rec.Error) case !rec.X86 || current == "tsc": diff --git a/internal/emu/clocksource_test.go b/internal/emu/clocksource_test.go index 79b4b59..bc178a4 100644 --- a/internal/emu/clocksource_test.go +++ b/internal/emu/clocksource_test.go @@ -275,3 +275,20 @@ func TestCheckClocksourceRecordUnwritable(t *testing.T) { t.Errorf("log = %s", fmt.Sprint(*lines)) } } + +// The clock source is read as root: the adb shell user is denied this sysfs directory on the Android 14 images +// (ModAVD/ModAVD#110; every boot of the 0.41.0 candidate logged "Permission denied"). +func TestClocksourceScriptRunsAsRoot(t *testing.T) { + if !strings.HasPrefix(clocksourceScript, "su 0 sh -c '") || !strings.HasSuffix(clocksourceScript, "'") || + strings.Count(clocksourceScript, "'") != 2 { + t.Fatalf("clocksourceScript must be one su 0 sh -c '…' command: %q", clocksourceScript) + } +} + +func TestParseClocksourceDenied(t *testing.T) { + out := "cat: /sys/devices/system/clocksource/clocksource0/current_clocksource: Permission denied\n---\n" + + "cat: /sys/devices/system/clocksource/clocksource0/available_clocksource: Permission denied\n---\nx86_64\n" + if current, _, arch, ok := parseClocksource(out); ok || current != "" || arch != "x86_64" { + t.Fatalf("denied read parsed as current=%q arch=%q ok=%v", current, arch, ok) + } +} diff --git a/release/0.12.0.SHA256SUMS.txt b/release/0.12.0.SHA256SUMS.txt new file mode 100644 index 0000000..9dae9a4 --- /dev/null +++ b/release/0.12.0.SHA256SUMS.txt @@ -0,0 +1,3 @@ +320d5d6a6695a3f3264b32320e1789c7f6f89500581854d05bfea7838fb7168b ModAVD.exe +d8d34bd920c733d20064cfcbfe90c77d694f63f3dcbf2be1022c8e223edb9fb9 ModAVDManager-0.12.0-win-x64.zip +fbfbd8bb52e3215b732e1e97757de6a1868f145d13ba7ff151095d241a3e28b5 tools/modavd.exe diff --git a/release/0.12.0.md b/release/0.12.0.md new file mode 100644 index 0000000..4f03a6d --- /dev/null +++ b/release/0.12.0.md @@ -0,0 +1,62 @@ +# ModAVDManager v0.12.0 + +- `ModAVD.exe`:`320d5d6a6695a3f3264b32320e1789c7f6f89500581854d05bfea7838fb7168b` +- `tools/modavd.exe`:`fbfbd8bb52e3215b732e1e97757de6a1868f145d13ba7ff151095d241a3e28b5` + +随 ModAVD 0.41.0 进包。下面是相对 v0.11.0 的改动。这一版主要是:开机前发现截图共享内存还被别的程序拿着时,写出是哪个程序;新建时语言没设成不再算新建失败;开机后记下客体的时钟源,给排查变慢的问题取证。 + +## 新增 + +- 开机前,如果这台实例的截图共享内存还在(上次那台模拟器退出后,有程序一直拿着它,多半是 MAA 这类用截图的工具),记一条警告,写出拿着它的程序和 pid,开机照常进行。这时截图共享可能开不起来,用它截图的工具会变慢;把这些程序退出后再开机就好。问题包里也单独记一节:没在运行的实例还留着截图共享内存的,写出拿着它的程序;没有就不出这一节。(ModAVDManager#168,ModAVD/ModAVD#210) +- 每次开机后设置的最后一步,记下客体的时钟源,写进这次开机的日志。x86_64 客体的时钟源不是 tsc 时(这次开机期间整体会变慢,关机再开才恢复),记一条警告,马上存下客体的内核日志到 `logs\mas_-<时间>-clocksource.txt`,每台实例留最新 5 份。读不到时只记一句,不拦开机。问题包里实例那一节多一行最近一次开机的时钟源,并带上最新 3 份这类内核日志。这一条只是取证,不改时钟源本身。(ModAVDManager#171,ModAVD/ModAVD#110) + +## 变更 + +- 模拟器的构建号(`sdk\emulator\source.properties` 的 `Pkg.BuildId`、包清单 `modavd-manifest.json` 的 `build`)除了 `mas-N`,也认版本号的写法(比如 `0.42.0`),开机检查、装包和更新都一样。这是为以后的模拟器改用版本号当构建号做准备;ModAVD 0.41.0 的模拟器仍是 `mas-41`。(ModAVDManager#169) + +## 修复 + +- 新建实例时语言没设成,不再让这次新建算失败:新建照常完成并正常关机,语言留到下次冷启动再设。(ModAVDManager#167,ModAVD/ModAVD#206) +- 打开运行中实例的设置页时,读安卓里的语言和时区只读系统属性,不再往客体里推送程序、用 root 起进程。(ModAVDManager#167,ModAVD/ModAVD#206) + +## 给调用方(AUTO-MAS) + +- `protocol` 还是 2:没有删命令、参数和 `--json` 字段,没有新的错误码和退出码。调用方不用改。 +- `--json`:根信息多一个字符串 `build_id`(模拟器的 `Pkg.BuildId` 原样,比如 `mas-41`;不知道时省略)。整数 `build` 保留,构建号是 `mas-N` 时还是 N,以后是版本号写法时为 0。`update status` 的 `current_build` 以后也可能是版本号写法。(ModAVDManager#169) +- 新建时语言没设成,`create` 不再报失败,也不用 `create --resume N`;`regional ` 里照旧看得到 `pending_language`。(ModAVDManager#167) + +## 下载与核对 + +用 Go 1.26.5、`GOTOOLCHAIN=local` 运行仓库里的 `build.ps1`,能编出相同的字节。单独下载的包里还有 Go 的许可原文和文件清单。请用附件 `SHA256SUMS.txt` 核对 zip 和解压出的两个 exe,例如在 PowerShell 里运行 `Get-FileHash .\ModAVDManager-0.12.0-win-x64.zip -Algorithm SHA256`。 + +单独下载的管理器不会自己更新,新版本要手动替换;ModAVD 包里的那份随 ModAVD 整包一起更新。单独使用时请给 `--root` 或设置 `MODAVD_ROOT`,否则会提示找不到 ModAVD 根目录。不要把单独下载的 zip 解压到 ModAVD 根目录,覆盖整包里的文件。 + +## English + +Shipped in ModAVD 0.41.0. Changes since v0.11.0. This release mostly brings: before a boot, a screenshot shared-memory region still held by another program is reported with that program's name; a language that could not be set at creation no longer fails the creation; each boot records the guest's clock source, as evidence for slowdowns. + +### New + +- Before a boot, when the instance's screenshot shared-memory region still exists (a program kept holding it after the last emulator exited, most likely a screenshot tool such as MAA), a warning names the programs holding it and their pids; the boot goes on. Frame sharing may then not start, and tools that take screenshots through it get slower; quit those programs and boot again. The problem report has a section of its own for regions left by instances that are not running, with the programs holding them; without any, the section is not there. (ModAVDManager#168, ModAVD/ModAVD#210) +- As the last step of the setup after each boot, the guest's clock source is written to that boot's log. When an x86_64 guest's clock source is not tsc (everything is slower until the instance is shut down and started again), a warning is logged and the guest's kernel log is saved right away to `logs\mas_-