新增 landscape_proof.ps1:V-C1~C7 判据(自动横屏 <=2s、旋转零中断、 动画桌面下 >=45fps、控制条自动隐藏、黑边唤出零远端泄漏、四按钮可点、 方向四态循环、表单竖屏)。方向判定只解析 Display: mDisplayId=0 块内 cur=WxH——这台 vivo 无 mRotation= 行,且 dumpsys 里有常驻 1080x2400 的 虚拟屏,匹配首个 cur= 会在已横屏时误报未旋转(探针实测踩过)。 横屏自动隐藏与既有 proof 的判据自冲突(HUD 随控制条 GONE,RTT= 读不到 被误判断线/未连接),三个 proof 统一加「确认隐藏态才走黑边唤醒」: phone_chain_proof、ime_chinese_proof、rel_mouse_phone_proof。 修 rel_mouse_phone_proof 两处: - 2c 基线判据:横屏滑动 768px 的残余相对事件成批延迟落地,旧的「两次 读数相等」被突发间隙骗过(实测假失败 351/1418px)。改为 6 读数沉降 + 4 次采样判漂移 + 绝对泄漏点特征比对。 - step 4 靶窗启动缺 3 个 Mandatory 参数,子进程瞬间失败退出(3b 起一直 如此,右键落在裸屏幕中心,靠近违反 R-11「不得注入他人窗口」)。现按 契约启动并把指针停在自家窗口内。 R-5 同源排查:靶窗结果目录固定路径 + 启动时删自己的 stop.flag,会让 上一轮失败遗留的靶窗永远收不到退休信号、继续抢前台并写脏 received.txt (实测收到 abc\nabc)。5 个 proof 统一改每轮唯一目录(\47512 后缀)。 ime_chinese_proof 的切换键标定失效:设备 IME 已换成搜狗(原为讯飞), 旧坐标 (146,2096) 静默点空。IME 按键视图对 uiautomator 不可见、无法机械 标定,按 R-9 转人工确认,不硬凑自动化。
198 lines
8.3 KiB
PowerShell
198 lines
8.3 KiB
PowerShell
# 2b V7 (device side): manual emoji acceptance helper.
|
|
# ASCII-only source (PS 5.1 + BOM-less = GBK corruption; see personal lessons).
|
|
#
|
|
# WHY MANUAL: the IME's own emoji panel is invisible to uiautomator (dump shows
|
|
# only our app's nodes), so adb cannot drive emoji selection. The human taps the
|
|
# emoji; this script only prepares the receiving window and reads back what
|
|
# actually arrived - the judgement is still made against real received bytes.
|
|
#
|
|
# What this proves: the phone IME commits a supplementary-plane character
|
|
# (surrogate pair) as ONE commitText, and protocol v3 delivers both code units
|
|
# adjacently so Windows synthesises a single character.
|
|
# The Windows half is already proven mechanically (V7' in text_injection_proof).
|
|
#
|
|
# Usage:
|
|
# powershell -ExecutionPolicy Bypass -File tools\emoji_manual_proof.ps1
|
|
# ... then, when prompted, tap an emoji on the phone's keyboard.
|
|
|
|
param(
|
|
# PC (controlled side) LAN address; DHCP-assigned - pass -PhoneIp when it
|
|
# moves (see ime_chinese_proof.ps1 for the 2026-10-31 stale-default lesson).
|
|
[string]$PhoneIp = "192.168.1.110",
|
|
[int]$Port = 47800,
|
|
[string]$Code = "t",
|
|
[int]$WaitSeconds = 120
|
|
)
|
|
$ErrorActionPreference = "Stop"
|
|
$repo = (Resolve-Path "$PSScriptRoot\..").Path
|
|
$adb = "D:\working\android\sdk\platform-tools\adb.exe"
|
|
# UNIQUE per run: a fixed path lets the startup Remove-Item below delete the
|
|
# stop.flag that leftover targets from a previous FAILED run are watching, so
|
|
# they never retire and keep writing into the shared received.txt
|
|
# (same trap fixed in phone_chain_proof, measured 2026-11-01).
|
|
$tmp = "$env:TEMP\remote-emoji-proof-$PID"
|
|
|
|
New-Item -ItemType Directory -Force -Path $tmp | Out-Null
|
|
$pidFile = "$tmp\target.pid"
|
|
$stopFile = "$tmp\stop.flag"
|
|
$resultFile = "$tmp\received.txt"
|
|
Remove-Item $pidFile,$stopFile,$resultFile -ErrorAction SilentlyContinue
|
|
|
|
Add-Type -TypeDefinition @"
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
public static class FgEmoji {
|
|
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
|
}
|
|
"@
|
|
|
|
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 Wait-File {
|
|
param([string]$Path, [int]$TimeoutMs = 20000)
|
|
$t = 0
|
|
while (-not (Test-Path $Path) -and $t -lt ($TimeoutMs / 150)) { Start-Sleep -Milliseconds 150; $t++ }
|
|
if (-not (Test-Path $Path)) { throw "timeout waiting for $Path" }
|
|
}
|
|
|
|
function Read-Received {
|
|
if (-not (Test-Path $resultFile)) { return "" }
|
|
$b = [System.IO.File]::ReadAllBytes($resultFile)
|
|
$s = [System.Text.Encoding]::UTF8.GetString($b)
|
|
if ($s.Length -gt 0 -and $s[0] -eq [char]0xFEFF) { $s = $s.Substring(1) }
|
|
return $s
|
|
}
|
|
|
|
function Show-Cps([string]$s) {
|
|
($s.ToCharArray() | ForEach-Object { "U+{0:X4}" -f [int]$_ }) -join " "
|
|
}
|
|
|
|
Write-Host "=== 1. retire earlier targets, ensure fresh agent ==="
|
|
$flags = @(Get-ChildItem -Path $env:TEMP -Recurse -Filter "stop.flag" -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.FullName -like "*remote*proof*" })
|
|
foreach ($f in $flags) { (Get-Item $f.FullName).LastWriteTime = Get-Date }
|
|
if ($flags.Count -gt 0) { Start-Sleep -Seconds 3 }
|
|
|
|
$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",$Port,"--code",$Code -WindowStyle Hidden
|
|
Start-Sleep -Seconds 1
|
|
Write-Host " agent started"
|
|
}
|
|
|
|
Write-Host "=== 2. target window (receives the injected text) ==="
|
|
Start-Process powershell.exe -ArgumentList "-NoProfile","-ExecutionPolicy","Bypass",`
|
|
"-File","$repo\tools\text_target.ps1","-PidFile",$pidFile,`
|
|
"-ResultFile",$resultFile,"-StopFile",$stopFile,"-GrabForeground"
|
|
Wait-File $pidFile
|
|
$parts = @((Get-Content $pidFile -Raw) -split "`n")
|
|
$tid = [int]$parts[0].Trim()
|
|
$hwnd = [IntPtr][long]$parts[1].Trim()
|
|
Start-Sleep -Milliseconds 800
|
|
$fg = [FgEmoji]::GetForegroundWindow()
|
|
Write-Host " target pid=$tid hwnd=$($hwnd.ToInt64()) foreground=$($fg -eq $hwnd)"
|
|
|
|
Write-Host "=== 3. phone session + show keyboard ==="
|
|
[void](Adb-Shell @("am","force-stop","com.verify.remote"))
|
|
[void](Adb-Shell @("am","start","-n","com.verify.remote/.MainActivity",
|
|
"--es","host",$PhoneIp,"--ei","port","$Port","--es","code",$Code))
|
|
$deadline = (Get-Date).AddSeconds(25)
|
|
$ui = ""
|
|
while ((Get-Date) -lt $deadline) {
|
|
Start-Sleep -Milliseconds 900
|
|
[void](Adb-Shell @("uiautomator","dump","/sdcard/ui.xml"))
|
|
$old = $ErrorActionPreference; $ErrorActionPreference = "Continue"
|
|
try { & $adb pull /sdcard/ui.xml "$tmp\ui.xml" 2>$null | Out-Null } finally { $ErrorActionPreference = $old }
|
|
if (Test-Path "$tmp\ui.xml") { $ui = [System.IO.File]::ReadAllText("$tmp\ui.xml", [System.Text.Encoding]::UTF8) }
|
|
if ($ui -match "RTT=") { break }
|
|
}
|
|
if ($ui -notmatch "RTT=") { throw "phone did not connect" }
|
|
|
|
$btns = [regex]::Matches($ui, '<node[^>]*class="android\.widget\.Button"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"')
|
|
if ($btns.Count -lt 1) { throw "keyboard button not found" }
|
|
$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 "IME not bound to our InputConnection" }
|
|
Write-Host " IME shown and bound: $([regex]::Match($ime,'mCurId=(\S+)').Groups[1].Value)"
|
|
|
|
$baseline = Read-Received
|
|
Write-Host ""
|
|
Write-Host " ------------------------------------------------------------"
|
|
Write-Host " NOW ON THE PHONE: tap the emoji key, then tap ONE emoji."
|
|
Write-Host " (Keyboard is up; the Windows target window is receiving.)"
|
|
Write-Host " Waiting up to $WaitSeconds s for a supplementary-plane character..."
|
|
Write-Host " ------------------------------------------------------------"
|
|
Write-Host ""
|
|
|
|
$deadline = (Get-Date).AddSeconds($WaitSeconds)
|
|
$got = ""
|
|
$found = $false
|
|
while ((Get-Date) -lt $deadline) {
|
|
Start-Sleep -Milliseconds 500
|
|
$got = Read-Received
|
|
if ($got.Length -gt $baseline.Length) {
|
|
$new = $got.Substring($baseline.Length)
|
|
# any code unit in the surrogate range means an emoji (or other
|
|
# supplementary-plane char) reached us
|
|
foreach ($ch in $new.ToCharArray()) {
|
|
$cp = [int]$ch
|
|
if ($cp -ge 0xD800 -and $cp -le 0xDFFF) { $found = $true; break }
|
|
}
|
|
if ($found) { break }
|
|
}
|
|
}
|
|
|
|
$final = Read-Received
|
|
$newFinal = if ($final.Length -ge $baseline.Length) { $final.Substring($baseline.Length) } else { "" }
|
|
Write-Host "=== 4. result ==="
|
|
Write-Host " baseline : [$(Show-Cps $baseline)]"
|
|
Write-Host " final : [$(Show-Cps $final)]"
|
|
Write-Host " new chars: [$(Show-Cps $newFinal)]"
|
|
Write-Host " new units: $($newFinal.Length)"
|
|
|
|
if ($found) {
|
|
# verify the pair is adjacent and forms a real code point
|
|
$units = $newFinal.ToCharArray() | ForEach-Object { [int]$_ }
|
|
$paired = $false
|
|
for ($i = 0; $i -lt $units.Count - 1; $i++) {
|
|
if ($units[$i] -ge 0xD800 -and $units[$i] -le 0xDBFF -and
|
|
$units[$i+1] -ge 0xDC00 -and $units[$i+1] -le 0xDFFF) {
|
|
$cp = 0x10000 + (($units[$i] - 0xD800) * 0x400) + ($units[$i+1] - 0xDC00)
|
|
Write-Host (" V7 PASS: adjacent surrogate pair -> U+{0:X4} ({1} code units committed as one)" -f $cp, $newFinal.Length)
|
|
$paired = $true
|
|
break
|
|
}
|
|
}
|
|
if (-not $paired) { throw "V7 FAIL: surrogate units present but not an adjacent valid pair" }
|
|
if ($newFinal.Contains([char]0xFFFD)) { throw "V7 FAIL: U+FFFD present (pair was split)" }
|
|
}
|
|
else {
|
|
Write-Host " V7 INCONCLUSIVE: no supplementary-plane character arrived within $WaitSeconds s"
|
|
Write-Host " (If you tapped an emoji and nothing arrived, that IS the finding - record it.)"
|
|
}
|
|
|
|
Write-Host "=== 5. cleanup ==="
|
|
[void](Adb-Shell @("input","keyevent","4"))
|
|
New-Item -ItemType File -Force -Path $stopFile | Out-Null
|
|
Start-Sleep -Milliseconds 500
|
|
Write-Host "=== done ==="
|