From bef3fbbc6ec6e400d5638f2fdec29f9e18910e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yank=C4=B1=20K=C3=BC=C3=A7=C3=BCk?= <12395430+yankikucuk@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:15:05 +0300 Subject: [PATCH] fix(adb): keep a bare numeric wm density as the physical density (#4074) Older Android builds print `wm density` as a bare number. The parser's documentation named that form, but only the labeled `Physical density:` and `Override density:` lines were matched, so a bare `420` left `physical_density` null in successful display Evidence with no limitation saying the declaration was unread (#3942). The parser now reads the first bare numeric line as the physical density when no labeled physical declaration is present; a labeled declaration still wins so a mixed dump cannot downgrade the value. Override density and physical size are unchanged. Validation: parser unit tests for the labeled, bare, CRLF, mixed and unrecognized forms, plus a provider-level regression through the inspect_adb_display operation; two of them fail on main and pass here. The issue's three CLI controls through `rea inspect-adb-display` against a stub adb return physical density 420 for the bare form and keep the labeled and override controls. Typecheck, lint and module boundaries pass. Closes #3942 --- src/adb/AdbProvider.test.ts | 20 +++++++++++++++ src/adb/AdbShellOutput.test.ts | 45 +++++++++++++++++++++++++++++++++- src/adb/AdbShellOutput.ts | 13 ++++++++-- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/adb/AdbProvider.test.ts b/src/adb/AdbProvider.test.ts index 5485d8a19..e3bc09603 100644 --- a/src/adb/AdbProvider.test.ts +++ b/src/adb/AdbProvider.test.ts @@ -1280,6 +1280,26 @@ it("inspects the display and window focus", async () => { ); }); +// Older builds print `wm density` as a bare number; the display projection +// must keep it as the physical density (morluto/rea#3942). +it("keeps a bare numeric wm density as the physical density", async () => { + const { binary } = await writeAdbStub( + shellStub({ + "wm:size": "Physical size: 1080x2340\n", + "wm:density": "420\n", + }), + ); + const display = await evidenceResult(providerOver(binary), { + operation: "inspect_adb_display", + input: { serial: "emulator-5554" }, + }); + expect(display).toMatchObject({ + physical_size: { width: 1080, height: 2340 }, + physical_density: 420, + override_density: null, + }); +}); + it("reads Android 16 focus declarations from displays when windows omits them", async () => { const { binary } = await writeAdbStub( shellStub({ diff --git a/src/adb/AdbShellOutput.test.ts b/src/adb/AdbShellOutput.test.ts index 1b9ad1f0b..20182153e 100644 --- a/src/adb/AdbShellOutput.test.ts +++ b/src/adb/AdbShellOutput.test.ts @@ -1,6 +1,49 @@ import { describe, expect, it } from "vitest"; -import { parseAmStartOutput } from "./AdbShellOutput.js"; +import { parseAmStartOutput, parseWmDensityOutput } from "./AdbShellOutput.js"; + +describe("parseWmDensityOutput", () => { + it("reads labeled physical and override densities", () => { + expect( + parseWmDensityOutput("Physical density: 420\nOverride density: 480\n"), + ).toEqual({ physical: 420, override: 480 }); + expect(parseWmDensityOutput("Physical density: 420\n")).toEqual({ + physical: 420, + override: null, + }); + }); + + // Older builds print the physical density as a bare number + // (morluto/rea#3942); it must not be lost as an unread declaration. + it("reads a bare numeric density as the physical density", () => { + expect(parseWmDensityOutput("420\n")).toEqual({ + physical: 420, + override: null, + }); + expect(parseWmDensityOutput("420\r\n")).toEqual({ + physical: 420, + override: null, + }); + }); + + it("prefers a labeled physical density over a bare number", () => { + expect(parseWmDensityOutput("560\nPhysical density: 420\n")).toEqual({ + physical: 420, + override: null, + }); + }); + + it("reports no density for unrecognized output", () => { + expect(parseWmDensityOutput("")).toEqual({ + physical: null, + override: null, + }); + expect(parseWmDensityOutput("420dpi\n")).toEqual({ + physical: null, + override: null, + }); + }); +}); const starting = "Starting: Intent { act=android.intent.action.MAIN cmp=com.example/.Main }"; diff --git a/src/adb/AdbShellOutput.ts b/src/adb/AdbShellOutput.ts index 90ed6f99d..118dc05a2 100644 --- a/src/adb/AdbShellOutput.ts +++ b/src/adb/AdbShellOutput.ts @@ -125,6 +125,7 @@ const SIZE_PAIR = /^Physical size:\s+(\d+)x(\d+)$/u; const SIZE_OVERRIDE = /^Override size:\s+(\d+)x(\d+)$/u; const DENSITY_PHYSICAL = /^Physical density:\s+(\d+)$/u; const DENSITY_OVERRIDE = /^Override density:\s+(\d+)$/u; +const DENSITY_BARE = /^(\d+)$/u; /** Parse `wm size` output into physical and optional override sizes. */ export const parseWmSizeOutput = ( @@ -153,7 +154,12 @@ export const parseWmSizeOutput = ( return { physical, override }; }; -/** Parse `wm density` output. Older builds print a bare number. */ +/** + * Parse `wm density` output. Current builds print labeled physical and + * optional override lines; older builds print the physical density as a + * bare number on its own line (morluto/rea#3942). A labeled declaration + * wins over a bare one so a mixed dump cannot downgrade the physical value. + */ export const parseWmDensityOutput = ( stdout: string, ): { @@ -162,14 +168,17 @@ export const parseWmDensityOutput = ( } => { let physical = null; let override = null; + let bare = null; for (const line of stdout.split(/\r?\n/u)) { const trimmed = line.trim(); const physicalMatch = DENSITY_PHYSICAL.exec(trimmed); if (physicalMatch !== null) physical = Number(physicalMatch[1]!); const overrideMatch = DENSITY_OVERRIDE.exec(trimmed); if (overrideMatch !== null) override = Number(overrideMatch[1]!); + const bareMatch = DENSITY_BARE.exec(trimmed); + if (bareMatch !== null && bare === null) bare = Number(bareMatch[1]!); } - return { physical, override }; + return { physical: physical ?? bare, override }; }; /** Parse focus declarations from `dumpsys window windows` or `displays`. */