32 lines
2.8 KiB
PowerShell
32 lines
2.8 KiB
PowerShell
param([Parameter(Mandatory)][int[]]$Contacts, [int]$Port=8554)
|
|
$ErrorActionPreference='Stop'
|
|
if ($Contacts.Count -eq 0 -or $Contacts.Count % 4 -ne 0) { throw 'Contacts must contain repeated x,y,id,pressure quadruples.' }
|
|
[AppContext]::SetSwitch('System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport', $true)
|
|
$discovery=Get-ChildItem -LiteralPath (Join-Path $env:LOCALAPPDATA 'Temp/avd/running') -Filter 'pid_*.ini' | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
|
$tokenLine=Get-Content -LiteralPath $discovery.FullName | Where-Object { $_ -match '^grpc.token=' } | Select-Object -First 1
|
|
if (!$tokenLine) { throw 'Authenticated emulator discovery is required; tokens are never logged.' }
|
|
function Varint([int]$Value) {
|
|
[uint32]$number=$Value; $bytes=[Collections.Generic.List[byte]]::new()
|
|
while ($number -ge 128) { $bytes.Add([byte](($number -band 127) -bor 128)); $number=$number -shr 7 }
|
|
$bytes.Add([byte]$number); return ,$bytes.ToArray()
|
|
}
|
|
$message=[Collections.Generic.List[byte]]::new()
|
|
for ($offset=0; $offset -lt $Contacts.Count; $offset+=4) {
|
|
# AVD touch injection uses portrait physical coordinates even when the app is landscape-left.
|
|
$fields=@((1080-$Contacts[$offset+1]),$Contacts[$offset],$Contacts[$offset+2],$Contacts[$offset+3])
|
|
$touch=[Collections.Generic.List[byte]]::new()
|
|
for ($field=0; $field -lt 4; $field++) { $touch.Add([byte](8*($field+1))); $touch.AddRange([byte[]](Varint $fields[$field])) }
|
|
$message.Add(10); $message.AddRange([byte[]](Varint $touch.Count)); $message.AddRange($touch.ToArray())
|
|
}
|
|
$frame=[byte[]]::new($message.Count+5); $frame[1]=($message.Count -shr 24) -band 255; $frame[2]=($message.Count -shr 16) -band 255
|
|
$frame[3]=($message.Count -shr 8) -band 255; $frame[4]=$message.Count -band 255; $message.ToArray().CopyTo($frame,5)
|
|
$client=[Net.Http.HttpClient]::new(); $request=[Net.Http.HttpRequestMessage]::new([Net.Http.HttpMethod]::Post,"http://127.0.0.1:$Port/android.emulation.control.EmulatorController/sendTouch")
|
|
$request.Version=[Version]::new(2,0); $request.VersionPolicy=[Net.Http.HttpVersionPolicy]::RequestVersionExact
|
|
$request.Headers.Add('TE','trailers'); $request.Headers.Authorization=[Net.Http.Headers.AuthenticationHeaderValue]::new('Bearer',$tokenLine.Substring('grpc.token='.Length))
|
|
$request.Content=[Net.Http.ByteArrayContent]::new($frame); $request.Content.Headers.ContentType=[Net.Http.Headers.MediaTypeHeaderValue]::new('application/grpc')
|
|
try {
|
|
$response=$client.SendAsync($request).GetAwaiter().GetResult(); $response.EnsureSuccessStatusCode() | Out-Null
|
|
$status=if ($response.TrailingHeaders.Contains('grpc-status')) { ($response.TrailingHeaders.GetValues('grpc-status') -join '') } else { '0' }
|
|
if ($status -ne '0') { throw "Emulator sendTouch grpc-status=$status" }
|
|
} finally { $request.Dispose(); $client.Dispose() }
|