2026-10-30 01:06:08 -04:00
|
|
|
# 2b evidence capture: byte-safe device screenshots of each acceptance state.
|
|
|
|
|
# ASCII-only source. Screenshots go through tools\shot.ps1 because PS 5.1 `>`
|
|
|
|
|
# writes UTF-16 and corrupts PNG bytes (see that file's header).
|
|
|
|
|
# Text/state assertions use uiautomator (lessons R-6: vivo logcat is throttled).
|
|
|
|
|
|
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
|
$repo = (Resolve-Path "$PSScriptRoot\..").Path
|
|
|
|
|
$ev = "D:\working\projects\yuankong\evidence\2b"
|
|
|
|
|
$adb = "D:\working\android\sdk\platform-tools\adb.exe"
|
2026-10-31 03:57:57 -04:00
|
|
|
# PC (controlled side) LAN address; DHCP-assigned. 2026-10-31: the stale
|
|
|
|
|
# .180 default silently cost proof runs after the office network moved to
|
|
|
|
|
# .110 - pass -PhoneIp (param) or update this line when it changes again.
|
|
|
|
|
# The 192.168.1.199 in step 2 is INTENTIONALLY unreachable (V5 error path).
|
|
|
|
|
$phoneIp = "192.168.1.110"
|
2026-10-30 01:06:08 -04:00
|
|
|
|
|
|
|
|
function Adb-Shell {
|
|
|
|
|
param([Parameter(Mandatory)][string[]]$Cmd)
|
|
|
|
|
$old = $ErrorActionPreference
|
|
|
|
|
$ErrorActionPreference = "Continue"
|
|
|
|
|
try {
|
|
|
|
|
$out = & $adb shell @Cmd 2>$null
|
|
|
|
|
return @{ out = ($out -join "`n"); code = $LASTEXITCODE }
|
|
|
|
|
}
|
|
|
|
|
finally { $ErrorActionPreference = $old }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Get-Ui {
|
|
|
|
|
[void](Adb-Shell @("uiautomator", "dump", "/sdcard/ui.xml"))
|
|
|
|
|
$old = $ErrorActionPreference
|
|
|
|
|
$ErrorActionPreference = "Continue"
|
|
|
|
|
try { & $adb pull /sdcard/ui.xml "$env:TEMP\cap-ui.xml" 2>$null | Out-Null }
|
|
|
|
|
finally { $ErrorActionPreference = $old }
|
|
|
|
|
if (-not (Test-Path "$env:TEMP\cap-ui.xml")) { return "" }
|
|
|
|
|
return [System.IO.File]::ReadAllText("$env:TEMP\cap-ui.xml", [System.Text.Encoding]::UTF8)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Wait-Live([int]$Seconds) {
|
|
|
|
|
$deadline = (Get-Date).AddSeconds($Seconds)
|
|
|
|
|
while ((Get-Date) -lt $deadline) {
|
|
|
|
|
Start-Sleep -Milliseconds 900
|
|
|
|
|
$x = Get-Ui
|
|
|
|
|
if ($x -match "RTT=") { return $x }
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Shot([string]$name) {
|
|
|
|
|
$out = Join-Path $ev $name
|
|
|
|
|
& powershell -NoProfile -ExecutionPolicy Bypass -File "$repo\tools\shot.ps1" -Out $out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
New-Item -ItemType Directory -Force -Path $ev | Out-Null
|
|
|
|
|
# drop the earlier broken captures (PS 5.1 '>' wrote them as UTF-16)
|
|
|
|
|
Get-ChildItem $ev -Filter "*.png" | ForEach-Object {
|
|
|
|
|
$b = [System.IO.File]::ReadAllBytes($_.FullName)
|
|
|
|
|
if ($b.Length -lt 4 -or ($b[0] -ne 0x89)) { Remove-Item $_.FullName -Force }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Write-Host "=== ensure fresh v3 agent ==="
|
|
|
|
|
$agentExe = "$repo\target\release\agent.exe"
|
|
|
|
|
$ag = Get-Process agent -ErrorAction SilentlyContinue
|
|
|
|
|
if ($ag -and (Get-Item $agentExe).LastWriteTime -gt $ag.StartTime) {
|
|
|
|
|
Stop-Process -Name agent -Force -ErrorAction SilentlyContinue
|
|
|
|
|
Start-Sleep -Milliseconds 800; $ag = $null
|
|
|
|
|
}
|
|
|
|
|
if (-not $ag) {
|
|
|
|
|
Start-Process $agentExe -ArgumentList "--port","47800","--code","t" -WindowStyle Hidden
|
|
|
|
|
Start-Sleep -Seconds 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Write-Host "=== 1. connect form (V4 entry state) ==="
|
|
|
|
|
[void](Adb-Shell @("am","force-stop","com.verify.remote"))
|
|
|
|
|
[void](Adb-Shell @("am","start","-n","com.verify.remote/.MainActivity"))
|
|
|
|
|
Start-Sleep -Seconds 3
|
|
|
|
|
$ui = Get-Ui
|
|
|
|
|
if ($ui -notmatch "android\.widget\.EditText") { throw "form fields missing" }
|
|
|
|
|
Shot "4b-form.png"
|
|
|
|
|
|
|
|
|
|
Write-Host "=== 2. failure path (V5): unreachable host via intent ==="
|
|
|
|
|
[void](Adb-Shell @("am","force-stop","com.verify.remote"))
|
|
|
|
|
[void](Adb-Shell @("am","start","-n","com.verify.remote/.MainActivity",
|
|
|
|
|
"--es","host","192.168.1.199","--ei","port","47800","--es","code","t"))
|
|
|
|
|
# Measured 2026-10-30: an unreachable host returns to the form with a concrete
|
|
|
|
|
# reason after ~32s (quinn's default connect timeout). Budget 50s, not 30s -
|
|
|
|
|
# the earlier 30s budget made this step look like a failure when it was only
|
|
|
|
|
# still inside the timeout. UX follow-up (logged in the result record): during
|
|
|
|
|
# those 30s the panel says only "connecting" with no cancel affordance.
|
|
|
|
|
$deadline = (Get-Date).AddSeconds(50)
|
|
|
|
|
$err = ""
|
|
|
|
|
while ((Get-Date) -lt $deadline) {
|
|
|
|
|
Start-Sleep -Milliseconds 1000
|
|
|
|
|
$ui = Get-Ui
|
|
|
|
|
if ($ui -match "android\.widget\.EditText") {
|
|
|
|
|
$shown = [regex]::Matches($ui, 'text="([^"]+)"') | ForEach-Object { $_.Groups[1].Value }
|
|
|
|
|
$err = $shown -join " | "
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($err) { Shot "4b-v5-error.png" } else { throw "V5: never returned to form with an error" }
|
|
|
|
|
Write-Host " error state visible: yes"
|
|
|
|
|
|
|
|
|
|
Write-Host "=== 3. live session (V4 via intent) ==="
|
|
|
|
|
[void](Adb-Shell @("am","force-stop","com.verify.remote"))
|
|
|
|
|
[void](Adb-Shell @("am","start","-n","com.verify.remote/.MainActivity",
|
|
|
|
|
"--es","host",$phoneIp,"--ei","port","47800","--es","code","t"))
|
|
|
|
|
$ui = Wait-Live 25
|
|
|
|
|
if (-not $ui) { throw "session never became live" }
|
|
|
|
|
Shot "4b-connected-stream.png"
|
|
|
|
|
Write-Host " $([regex]::Match($ui,'text="([^"]*RTT=[^"]*)"').Groups[1].Value)"
|
|
|
|
|
|
|
|
|
|
Write-Host "=== 4. keyboard shown (real IME bound to our InputConnection) ==="
|
|
|
|
|
$btns = [regex]::Matches($ui, '<node[^>]*class="android\.widget\.Button"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"')
|
|
|
|
|
if ($btns.Count -lt 1) { throw "no keyboard button" }
|
|
|
|
|
$b0 = $btns[0]
|
|
|
|
|
$kx = [int](([int]$b0.Groups[1].Value + [int]$b0.Groups[3].Value) / 2)
|
|
|
|
|
$ky = [int](([int]$b0.Groups[2].Value + [int]$b0.Groups[4].Value) / 2)
|
|
|
|
|
[void](Adb-Shell @("input","tap","$kx","$ky"))
|
|
|
|
|
Start-Sleep -Seconds 3
|
|
|
|
|
$ime = (Adb-Shell @("dumpsys","input_method")).out
|
|
|
|
|
if ($ime -notmatch "mInputShown=true") { throw "IME not shown" }
|
|
|
|
|
if ($ime -notmatch "mHaveConnection=true") { throw "no InputConnection bound" }
|
|
|
|
|
Shot "4b-ime-shown.png"
|
|
|
|
|
|
|
|
|
|
Write-Host "=== 5. IME commit reaches the pipeline (diagnostic shows commit) ==="
|
|
|
|
|
[void](Adb-Shell @("input","text","hello"))
|
|
|
|
|
Start-Sleep -Milliseconds 1200
|
|
|
|
|
[void](Adb-Shell @("input","keyevent","62")) # SPACE: accept any pending candidate
|
|
|
|
|
Start-Sleep -Seconds 2
|
|
|
|
|
$ui = Get-Ui
|
|
|
|
|
$diag = [regex]::Match($ui, 'text="([^"]*RTT=[^"]*)"').Groups[1].Value
|
|
|
|
|
Write-Host " phone status: $diag"
|
|
|
|
|
Shot "4b-ime-committed.png"
|
|
|
|
|
|
|
|
|
|
Write-Host "=== 6. disconnect (back key) ==="
|
|
|
|
|
[void](Adb-Shell @("input","keyevent","4")) # dismiss keyboard
|
|
|
|
|
Start-Sleep -Milliseconds 800
|
|
|
|
|
[void](Adb-Shell @("input","keyevent","4")) # disconnect
|
|
|
|
|
Start-Sleep -Seconds 2
|
|
|
|
|
$ui = Get-Ui
|
|
|
|
|
if ($ui -match "RTT=") { throw "still connected after two BACK presses" }
|
|
|
|
|
Shot "4b-disconnected.png"
|
|
|
|
|
Write-Host "=== CAPTURE COMPLETE ==="
|
|
|
|
|
Get-ChildItem $ev -Filter "4b-*.png" | ForEach-Object {
|
|
|
|
|
$b = [System.IO.File]::ReadAllBytes($_.FullName)
|
|
|
|
|
$sig = ($b[0..3] | ForEach-Object { $_.ToString("X2") }) -join " "
|
|
|
|
|
"{0,-28} {1,9} {2}" -f $_.Name, $b.Length, $sig
|
|
|
|
|
}
|