Android physical devices through the host's default adb server
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 18/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- android, typescript
- Ambito
- mobile
Direzione di ricerca
Start with src/drivers/android/index.ts to see how AndroidDriver routes calls, then adb-server.ts for the existing probe and timeout pattern the spec reuses. The two new files are default-adb-server.ts (protocol gate) and physical-handler.ts; the spec names their functions and the reason codes. Done means each row of the input table gives the stated result against the fake driver, and a blocked protocol runs no adb command.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Part of #428.
Scope
After this PR, an operator can enroll a real Android phone, and agents can lease it, through the Android driver. The driver reaches phones through the host's default adb server (port 5037), always with -s <serial>. It inspects a phone for device add, reads which enrolled phones are present, reclaims a released phone by uninstalling user apps, and runs adb for device.exec on a leased phone. (The lease environment, ANDROID_SERIAL and ANDROID_ADB_SERVER_PORT=5037, is #438's.) Right before every adb command it sends for a phone, device.exec's included, it reads the default server's adb protocol version afresh. It sends nothing when that differs from Simlock's adb or cannot be read. doctor then names both versions. Tasks 1a–3 built the core side against the fake driver; this task is the real Android side of ADR 0022 §5–§8. The phone lane gets its Android test.
In short
Today, after tasks 1a–4, device add --platform android cannot enroll anything: the Android driver has no physical handler. After this PR, a phone plugged in by USB with USB debugging authorized can be enrolled, leased and reclaimed. Simlock never stops the host's adb server. Right before each Android physical command, Simlock checks that server's adb protocol version, and sends the command only when the server speaks Simlock's version or nothing listens on its port.
New terms:
- default adb server: the adb server on port 5037 that plain
adband Android Studio use, e.g. the oneadb devicestalks to on a Mac. Simlock's own server (port 5038, USB off) stays for emulators only. - USB transport: a line of
adb devices -lthat carries ausb:field, e.g.R58M123ABC device usb:1-1 product:o1s model:SM_G991B transport_id:3. A line without it (192.168.1.5:5555 device …) is a network transport. The transport decides; the serial's shape never does. - adb protocol version: the number an adb client and server must share, or the client kills the server and starts its own.
adb versionprints it last (Android Debug Bridge version 1.0.41is 41). The server answers it tohost:versionover its socket, which restarts nothing. - physical handler: the part of the Android driver that serves physical devices. The driver routes each call to it when the device is
physical: true.
flowchart LR
A["device add / grant / watcher / release / device.exec"] --> D["AndroidDriver"]
D -->|"physical: false"| E["emulator side<br/>Simlock's server, unchanged"]
D -->|"physical: true"| P["physical handler (new)"]
P --> V{"host:version on 5037, before each command<br/>same protocol? (new)"}
V -->|yes| C["adb -P 5037 -s <serial> …"]
V -->|"no / unreadable"| N["no command: not present, refused;<br/>doctor names both versions"]
# before (tasks 1a-4 landed)
$ simlock device add --platform android R58M123ABC
Refused: this driver cannot read physical devices yet. (#437's placeholder, reason not-connected)
# after
$ simlock device add --platform android R58M123ABC
Enrolled SM-G991B (API 34), 12 user apps recorded.
$ simlock doctor # Android Studio's older adb runs the default server
driver-advisory android adb-protocol-mismatch: the adb server on port 5037 speaks protocol 40 and Simlock's adb speaks 41. …
| Input | Before (after task 4) | After |
|---|---|---|
device add --platform android R58M123ABC, phone on USB, USB debugging authorized |
refused | enrolled under R58M123ABC: ro.product.model, API level, user apps of the current user |
same, USB debugging not authorized (unauthorized) |
refused | refused: USB debugging not authorized (untrusted) |
device add --platform android 192.168.1.5:5555 (network transport only) |
refused | refused: only USB-connected devices can be enrolled (network-only) |
device add --platform android emulator-5554 (a developer's own emulator on the default server) |
refused | refused: only USB-connected devices can be enrolled (network-only) |
device add --platform android R58M123ABC, not listed, or listed offline |
refused | refused: not connected (not-connected) |
any device add --platform android … while the default server speaks another protocol |
refused | refused: not connected (not-connected), the message naming both protocol versions; no adb command runs |
device add --platform android R58M123ABC while a read fails or times out (host:version, adb devices -l, getprop, user id, package list) |
refused | refused: could not read the device, naming the read (unreadable) |
lease --platform android --physical, the enrolled phone free and present |
no phone enrollable | granted; environment ANDROID_SERIAL=R58M123ABC, ANDROID_ADB_SERVER_PORT=5037 (#438) |
| an enrolled phone unauthorized, or unplugged, for the grace period | stays ready (no real presence read) |
absent (untrusted), absent (not-present) |
| an enrolled phone with its screen locked | — | present: adb shows no lock state, so it stays in rotation |
| release of that lease | — | each user app not on the enrollment list uninstalled for the current user; listed apps found missing reported |
device.exec adb shell getprop on that lease |
— | runs adb -P 5037 -s R58M123ABC shell getprop |
device.exec adb kill-server (or start-server, connect, server, detach, … as the subcommand) on that lease |
— | refused |
device.exec adb reboot bootloader (or sideload, sideload-auto-reboot, recovery, fastboot after reboot) on that lease |
— | refused: it takes the phone off adb |
device.exec adb shell getprop on that lease while the default server speaks another protocol |
— | refused, naming both protocol versions; nothing runs |
device.exec adb shell dumpsys usb on that lease |
— | runs: a refused word after the subcommand is an operand |
| the default server switches to another protocol while the phone is leased | — | no command sent; the phone reads not present, so the lease ends device-lost after the grace period (task 3) |
simlock adb devices, lease --platform android |
Simlock's server, emulators | unchanged |
If this goes wrong, an operator would see an authorized phone refused, apps left on a released phone, Android Studio's adb server restarted, emulator leases stalling while a phone hangs, or a command run on a phone nobody enrolled.
Technical spec
Modules touched
flowchart LR
CORE["core callers: enrollment, grant, reclaim,<br/>watcher, device.exec (tasks 1b-4)"] --> AD["drivers/android/index.ts<br/>AndroidDriver *"]
DOC["core/doctor: advisories"] --> AD
DSP["daemon/dispatcher *<br/>(unsetEnv)"] --> AD
AD --> EM["emulator side: AdbServerSupervisor,<br/>AdbRegistrar (unchanged)"]
AD --> PH["drivers/android/physical-handler.ts (new)"]
PH --> PG["drivers/android/default-adb-server.ts (new)<br/>protocol gate"]
PH --> PR["ports: ProcessRunner"]
PG --> TCP["ports: TcpProbe, Filesystem"]
PG --> PR
EM --> PR
EM --> TCP
One device add through the hops, with the failure reply:
sequenceDiagram
participant C as CLI
participant D as Daemon (enrollment, task 1b)
participant P as Android physical handler
participant S as Default adb server :5037
participant R as Phone on USB
C->>D: device add --platform android R58M123ABC
D->>P: inspectPhysical("R58M123ABC")
P->>S: host:version (socket)
S-->>P: OKAY 0004 0029 (protocol 41)
alt same protocol as Simlock's adb
Note over P,S: a fresh host:version right before each adb command below
P->>S: adb -P 5037 devices -l
S-->>P: R58M123ABC device usb:1-1 …
P->>R: -s R58M123ABC: getprop, am get-current-user, pm list packages -3
R-->>P: model, API level, user id, apps
P-->>D: inspection
D-->>C: Enrolled SM-G991B (API 34), 12 user apps recorded
else other protocol (at any check), no USB transport in state device, or a failed read
P-->>D: refusal with its reason (unreadable for a failed read); no command named the serial before its line was read
D-->>C: Refused: <reason>
end
src/drivers/android/default-adb-server.ts(new): the protocol check ("the gate"),ensureAdbProtocol(). It is the one place that decides whether a physical command may run (architecture rule 10).- Simlock's protocol, read first: the last number of
adb version's first line (1.0.41→ 41). Read with Simlock'sadb, 30 s limit;adb versiontalks to no server. Cached with the adb binary'smodifiedAtMsandsize(Filesystem.stat,src/ports/filesystem.ts:167). Each check stats the binary and reads the version again when either changed, so a platform-tools update under a running daemon is seen. A failed read is not cached. - The server's protocol:
TcpProbe.send(5037, "000chost:version", 5000), a 5 s limit (theIDENTITY_TIMEOUT_MSprecedent inadb-server.ts:16). NoisListeningcall: it answersfalseon a timeout too (src/ports/tcp-probe.ts:53-57), which would fail open. A replyOKAY+0004+ four hex digits is the version. - Answer (safety rule 9 as amended, fail closed):
blockedwhen Simlock's own version cannot be read, whatever the server answers;openwhensendrejects withECONNREFUSED: nothing listens, and Simlock'sadbstarts a server of its own version, as anyadbdoes;openwhen both numbers match;blockedwhen they differ, whensendrejects with any other error, when it times out, and when the reply is anything else (an empty reply included).
- Every physical adb command awaits its own check right before it is sent: each
adb devices -l, eachgetprop, each user id read, eachpm list packages, eachpm uninstall, and the commandpassthroughreturns.advisories()runs one check too. Nothing keeps the server's answer: there is no stored verdict, no timer, and the driver needs nohealth.probeIntervalMs. A fixed server gets the next command, with no restart. - A
blockedcheck sends nothing for that command. What the calling method then answers is below, method by method. - Between a check and its command (milliseconds), another client can start a server of another protocol. Simlock's adb client then restarts it, as any adb client does. The KNOWN-PITFALLS entry below names this.
- Never sends
kill, never runskill-serverorstart-server, never signals a process. It sends nothing to 5037 buthost:version.
- Simlock's protocol, read first: the last number of
src/drivers/android/physical-handler.ts(new): every physical call. Each runsadb -P 5037, with the injected base environment plusANDROID_ADB_SERVER_PORT=5037, and withoutANDROID_ADB_SERVER_ADDRESS,ADB_SERVER_SOCKETorANDROID_SERIALfrom that base (M6): each of them could aim adb at another server or device. It never usesAndroidDriver#env(index.ts:713), which points at Simlock's server. Limits: 30 s per read, 60 s per uninstall (ADR 0022 §5; architecture rule 11). A read past its limit, or with no exit code, is a failed read. Physical calls take no per-device lock and no port allocation, so they never wait behind, or block, an emulator call.inspectPhysical(id): oneadb devices -l, after its check. The line whose serial equalsiddecides:- USB transport in state
device: inspected. Canonical ID is that serial. Then, with-s <serial>:shell getprop ro.product.model(model),shell getprop ro.build.version.sdk(OS version, the API level),shell am get-current-user(user id),shell pm list packages -3 --user <id>(enrollment app list). Class is alwaysphone(ADR 0022 §6: every Android device isphone). - The user id, here and in
reclaim, is a claim (safety rule 10): after trimming, it must be 1 to 6 digits, or the read fails. So no unchecked text reachesadb shell. - USB transport in state
unauthorized: refuseduntrusted, USB debugging not authorized. - Only network transports, or an emulator line (
emulator-5554 device …, nousb:field): refusednetwork-only, "only USB-connected devices can be enrolled". - Not listed, or another state (
offline,no permissions, …): refusednot-connected. - A check
blockedbecause the versions differ, before any of these commands: refusednot-connected, with a message naming both protocol versions. Noadbcommand is sent after that check. - A check
blockedbecause a version could not be read, before any of these commands: refusedunreadable, with a message naming the read: "Simlock's adb version" whenadb versiongave no protocol number, "the adb server's version on port 5037" when the server's answer is not a version, the check timed out, orsendrejected with anything butECONNREFUSED. These are the cases whereadvisories()reports (below). Noadbcommand is sent after that check. - A failed or timed-out
adb devices -l, or a failed read after the device was found (getprop, user id, package list): refusedunreadable, with a message naming the read. Nothing is written.inspectPhysicalnever rejects for a failed read. - The reason codes are #437's
DEVICE_NOT_ENROLLABLEreasons. - No command names the serial until its line is a USB transport in state
device.
- USB transport in state
listPresentPhysical(enrolledIds): oneadb devices -l, after its check. Its callers in the core: #438's grant, and #439's watcher, presence read after a failed reclaim, and paused quarantine retry. One entry per given serial, none for any other (safety rule 8 as amended): a serial that is not enrolled gets no entry and no command. Present: on a USB transport in statedevice, with its OS version fromgetprop ro.build.version.sdkon that serial (#439'sosVersion). Not presentuntrusted: stateunauthorized. Not presentnot-present: missing,offline, another state, or only on a network transport. A locked phone is present: adb shows no lock state (ADR 0022 §7). A failed or timed-out listing, a failedgetpropon a serial, or ablockedcheck answers the serials concerned not presentnot-presentand does not reject (ADR 0022 §5: a read past its limit is "not present"). A checkblockedbeforeadb devices -lconcerns every given serial; one before a serial'sgetpropconcerns that serial.reclaimof a physical device: read the user id and the user'spm list packages -3; for each package not onenrolledApps,shell pm uninstall --user <id> <package>, one at a time, stopping at the first failure; then list again. Each of these commands waits for its own check. The packages come from #438'suninstallPlan. Result: strategyuninstall,missingApps= enrolled packages not on the first list. When it rejects, the core reads presence and picksabsentorquarantined(task 3). It rejects, and sends no later command:- when a check is
blocked; - when the user id read fails, passes 30 s, or prints a bad user id (above): no
pm list packagesand no uninstall runs; - when the first list fails, passes 30 s, or has a bad line (below): no uninstall runs. It never resolves with an empty list;
- when an uninstall exits non-zero, prints anything but
Success, or passes 60 s; - when the second list fails, passes 30 s, or has a bad line, or still holds a package off the enrollment list.
- when a check is
- Package lines are a claim (safety rule 10). A trailing
\ris stripped, and blank lines are ignored. Every other line must bepackage:followed by one or more letters, digits,_and.only. Any other line,package:with no name included, fails the read, so no unchecked text reachesadb shell. passthrough("adb", <physical device>, args, context)(the signature task 4 left, ADR 0022 §5; #440 made it return a promise): it awaits its own check, then returnsadb -P 5037 -s <serial> <args>, envANDROID_ADB_SERVER_PORT=5037, andunsetEnvANDROID_ADB_SERVER_ADDRESS,ADB_SERVER_SOCKET,ANDROID_SERIAL(below). The dispatcher spawns it as soon as the promise resolves. Refused withPassthroughRefusedError, and nothing is spawned:- when its check is
blocked. The message names both protocol versions, or the version it could not read, asadvisories()does; - any caller argument before the subcommand (
-s,-t,-d,-e,-P,-H,-L,--one-device, any other): Simlock supplies the scope, so the command stays aimed at the leased phone. Soargs[0]is the subcommand; args[0]iskill-server,start-server,server,nodaemon,connect,disconnect,pair,reconnect,tcpip,usbordetach: each stops, starts, restarts or moves a server or transport other holders share, or takes the phone off USB for the next holder. The same word later in the line is an operand and runs (shell dumpsys usb);args[0]isreboot-bootloader(adb's older spelling ofreboot bootloader), orargs[0]isrebootandargs[1]isbootloader,sideload,sideload-auto-reboot,recoveryorfastboot: each takes the phone off adb. Any otherrebootruns;- bare
shellwith no terminal, as for emulators.
- when its check is
estimate({operation: "reclaim"}, <physical spec>): 90 000 ms at both clean levels: one 30 s read limit plus one 60 s uninstall limit. Two callers read it: every ready grant'sestimatedReclaimMs(src/leasing/lease-acquisition-coordinator.ts:1128), and doctor's stall rule for an unclaimed reclaim (stallThresholdMs,core/doctor.ts:973, via:908-909).
src/core/driver.tsPassthroughCommand(:439): an optionalunsetEnv: readonly string[].src/daemon/dispatcher.ts#deviceExec(:539, the env at:559) awaitspassthroughas #440 left it, builds the env as today,{ ...execEnv, ...command.env }, then deletes eachunsetEnvkey. TodayexecEnvis the daemon'sprocess.env(src/daemon/main.ts:436), so without this a daemon started withANDROID_ADB_SERVER_ADDRESSwould aim an agent's command at another server. Only the physical Android command sets it.src/drivers/android/index.ts:AndroidDriverroutes onphysicalto the handler forinspectPhysical,listPresentPhysical,reclaim,passthroughandestimate.leaseEnvironmentkeeps #438's physical answer.estimateandleaseEnvironmentsend nothing to 5037 and start no process. Every emulator path is unchanged.advisories()(new on this driver) runs one check, whether or not any Android device is enrolled. It returnsadb-protocol-mismatchwhen Simlock's own version cannot be read (whatever the server answers), when the server answers another version, when its answer is not a version, or when the check times out orsendrejects with anything butECONNREFUSED. It returns nothing on a match, and nothing onECONNREFUSED: nothing listens. The message says which of these it is: it names both protocol numbers, or "unreadable" for the one it could not read. It says no command goes to physical Android devices until they match, and that Simlock will not restart that server.dispose()is unchanged: it stops Simlock's own server and nothing on 5037.- The phone lane for Android, with task 4's two tags (
physicalandphysical-manual). Both files are skipped unlessSIMLOCK_PHYSICAL_ANDROIDnames the phone's USB serial andSIMLOCK_PHYSICAL_ANDROID_APKnames an APK to install. The skip message names the missing variable.e2e/slow-android-physical.test.ts(new), tagphysical: needs no person, runs unattended;e2e/slow-android-physical-manual.test.ts(new), tagphysical-manual: unplug and plug back. Each step is a prompt in the log, and the test waits up to 150 s for it. The hardware verifier never runs it: its toolchain row needs a person at the phone (below).
docs/internal/agent-rules/toolchain.md: in the slow-lane table task 4 extended, a row for the Android phone:command -v adbfinds it,adb devices -llists$SIMLOCK_PHYSICAL_ANDROIDon a USB transport in statedevice,SIMLOCK_PHYSICAL_ANDROID_APKnames an APK. The commands arescripts/slow-e2e.sh --physical e2e/slow-android-physical.test.tsand, by hand,scripts/slow-e2e.sh --physical-manual e2e/slow-android-physical-manual.test.ts. The manual file's row adds the machine check "a person at the phone", as #440's iOS row does:SIMLOCK_PHYSICAL_PERSON=1, set only by a person running it by hand. The hardware verifier never sets it, so it reports the manual fileunavailable (needs a person), its existing outcome.docs/CLI.md:- Prerequisites gain "Android physical devices": connected by USB, USB debugging authorized for this host. Simlock reaches them through the host's default adb server (port 5037), which it starts if none runs and never stops. Simlock cannot see an Android lock screen: a locked phone stays in rotation.
- The
doctorsection gains theadb-protocol-mismatchadvisory: what it means and that the fix is running that server with an adb of the same protocol.
docs/internal/KNOWN-PITFALLS.md: an entry with two parts.- A default server of another protocol, started between Simlock's check and the command right after it, is restarted by Simlock's adb client. adb offers no way to stop that. The window is milliseconds long, for Simlock's own calls and for
device.execalike. - The default server a physical
adbcommand starts is not in that command's process group. adb's fork-server callssetsid: on the maintainer's Mac,adb -L tcp:5037 fork-serverhas parent pid 1 and its own process group. So whenNodeProcessRunnerkills a timed-out physical command by its group (killProcessTree,src/ports/process-runner.ts:508), the server keeps running.
- A default server of another protocol, started between Simlock's check and the command right after it, is restarted by Simlock's adb client. adb offers no way to stop that. The window is milliseconds long, for Simlock's own calls and for
docs/internal/agent-rules/safety.mdrule 9 and ADR 0022 §5 and §12 already say the check runs right before every physical command (#443). This task makes the code match; it does not edit them.
Contract and event changes
None. PassthroughCommand.unsetEnv is an internal driver type, not a contract shape. Searched src/contract/ and src/bus/index.ts: the driver methods, PresentPhysical with osVersion and its reasons, ReclaimResult.missingApps, strategy uninstall, absentReason and the events were added by tasks 1b–3. The driver-advisory doctor finding exists (core/doctor.ts:88); its code is the driver's own text. No protocol bump.
Other code on the same state
The state: the default adb server on 5037, the enrolled phones on it, and Simlock's cached adb version. Line numbers are on main at ac4cf1e; tasks 1a–4 move them.
AdbServerSupervisor(src/drivers/android/adb-server.ts:104) owns Simlock's server. It refuses 5037 as its port (adb-server.ts:167), and starts its server withADB_USB=0(:43-49), so phones never appear there. The handler never uses Simlock's port. No overlap.- Every emulator call goes through
AndroidDriver#env(index.ts:713):listManaged(:1015) and#scanAdbSerials(:1067, matches onlyemulator-\d+),makeReady, emulatorreclaim(:932),passthroughwith no device (:650),leaseEnvironmentfor an emulator (:636). None reaches 5037; the handler reaches nothing else. Unchanged. #withDeviceLock(index.ts:2041) andPortAllocator(index.ts:2068) serialise emulator work. Physical calls take neither, so a hung phone call never delays an emulator call, and the reverse.NodeProcessRunner(src/ports/process-runner.ts:177) spawns every command detached, and kills a timed-out one by its process group (killProcessTree,:508). A physicaladbthat finds nothing on 5037 forks the default server, which leaves the group (setsid). So the kill ends the client and never the server. The KNOWN-PITFALLS entry names this. The phone lane proves only that a timed-out exec leaves a running server alone; the forked case rests on the observed process group, since forcing it would stop the host's server.AdbRegistrar(src/drivers/android/adb-registrar.ts:25) sendshost:emulatorto Simlock's port only. The gate sendshost:versionto 5037 only. Different servers.Doctor#collectAdvisories(src/core/doctor.ts:492) callsadvisories(); a rejecting call contributes nothing. Soadvisories()returns the mismatch as a finding and never rejects for it.- Core callers of the physical methods: enrollment (task 1b); grant presence check and reclaim (task 2); the physical-device watcher, the presence read after a failed reclaim, and the paused quarantine retry's presence read (task 3);
estimatefrom grant timing and doctor (above);device.execrouting (src/core/driver-catalog.ts:74,src/daemon/dispatcher.ts:539, as task 4 left them). The core's operation claim orders enrollment, reclaim and quarantine retries; the driver keeps no per-device state. The watcher's presence read can run while a reclaim uninstalls. Both are calls on one adb server; the read changes nothing on the phone. The core's claim decides the state, so the reclaim's outcome wins. - Other adb clients on the host (Android Studio, an agent's plain
adb, a second Simlock instance) share 5037 and see the phones. One of another protocol restarts the server, as adb does; Simlock's next check sees it, and sends nothing. Simlock never restarts it, except in the window the KNOWN-PITFALLS entry names. An agent's ownadbon its leased phone is the agent's; Simlock uninstalls only after release. - A
device.execstill running when the watcher marks the phone absent runs until it ends orexec.timeoutMsstops it, as for emulators today (KNOWN-PITFALLS "Adevice.execcommand is authorized once, at its start").
Rules in play
safety.mdrule 1 as amended: the only destructive act ispm uninstallof a user app off the enrollment list, for the current user. No reboot, erase, or command on a phone that is not enrolled, apart from the admin's owndevice addinspection.safety.mdrules 7, 8 as amended: presence reads only enrolled serials; a non-enrolled serial gets no entry and no command.safety.mdrule 9 as amended: the default server is never stopped by Simlock. Right before each physical command, Simlock checks that server's adb protocol version, and sends nothing when it differs or the server does not answer (#443's wording). The check fails closed: only a refused connection means nothing listens, and then adb starts a server. This task does not edit the rule.safety.mdrule 10:adb devices -llines, the user id and package lines are claims; the user id and package names are checked before they reachadb shell.architecture.mdrules 2, 10, 11: adb knowledge stays insrc/drivers/android/; the gate is the one place that allows a physical command; every physical wait is bounded.testing.mdrules 1–4: the "never" lines below assert over every physical method's calls, not one.
Tests
Seam: AndroidDriver.create with ScriptedProcessRunner, FakeTcpProbe and MemoryFilesystem, modelled on src/drivers/android/index.test.ts. A gate test rejects FakeTcpProbe.send with an error whose code is ECONNREFUSED, or another code. FakeTcpProbe records each send's port and payload today (src/ports/tcp-probe.ts:102-105) and gives every send one reply; it gains the send's limit and a reply per send, so a test can change the server's version between two commands of one call. The unsetEnv merge is tested in src/daemon/dispatcher.test.ts. Phone lane: withDaemon({ driver: "real" }) and the HTTP device.exec route, modelled on e2e/slow-android-smoke.test.ts and e2e/device-exec.test.ts.
- right before every
adbcommand a physical method sends (inspectPhysical,listPresentPhysical,reclaim, and the commandpassthroughreturns), the driver sends onehost:versionto port 5037 with a 5000 ms limit, and it sends nothing else to 5037 estimateandleaseEnvironmenton a physical device send nothing to 5037 and start no process- a server that turns to another protocol between two commands of one call gets no later command:
inspectPhysicalafteradb devices -lis refusednot-connectednaming both versions and runs nogetprop;listPresentPhysicalanswers that serial not presentnot-present;reclaimafter its first uninstall rejects and runs no second uninstall - two
passthroughcalls on a physical device send twohost:version: nothing keeps the server's answer. Afteradvisories()saw a mismatch, apassthroughwhose own check matches returns its command; afteradvisories()saw a match, apassthroughwhose own check sees another version is refused - no physical method, and no
passthroughresult, ever runskill-serverorstart-serveragainst 5037, anddispose()sends nothing to 5037 - with a default server of another protocol:
listPresentPhysicalanswers every given serial not presentnot-present,inspectPhysicalis refusednot-connectedwith a message naming both protocol numbers,reclaimrejects, and none of them starts a process afteradb version - a
host:versionreply that is notOKAY0004plus four hex digits, an empty reply (a timeout), asendthat rejects with an error other thanECONNREFUSED, and anadb versionwith no protocol number, each block physical commands like a mismatch - a
sendthat rejects withECONNREFUSED(nothing listens on 5037) lets physical commands run - an
adb versionwith no protocol number blocks physical commands even whensendrejects withECONNREFUSED inspectPhysicalis refusedunreadablenaming "Simlock's adb version" whenadb versionhas no protocol number, and naming "the adb server's version on port 5037" when the reply is not a version, is empty, orsendrejects with an error other thanECONNREFUSED- a server that matched again after a mismatch gets commands on the next call, and nothing restarts it
- Simlock's
adb versionruns once across several physical calls; after a failedadb version, the next call runs it again - after the adb binary's
modifiedAtMsorsizechanges, the next call runsadb versionagain and uses the new number advisories()returnsadb-protocol-mismatchnaming both protocol numbers on a mismatch; "unreadable" for the server's version when the reply is not a version, is empty, orsendrejects with an error other thanECONNREFUSED; "unreadable" for Simlock's version whenadb versionhas no protocol number, withsendrejectingECONNREFUSEDtoo; and nothing on a match or whensendrejects withECONNREFUSEDadvisories()reports the mismatch on a driver that has never been asked about a physical device (no Android device enrolled)- with
ANDROID_ADB_SERVER_ADDRESS,ADB_SERVER_SOCKETandANDROID_SERIALin the base environment, every physical command runsadb -P 5037withANDROID_ADB_SERVER_PORT=5037and none of the three; every device command carries-s <serial> inspectPhysicalof a serial on a USB transport in statedevicereturns that serial,ro.product.model, the API level, classphone, and the packagespm list packages -3 --user <id>lists for the idam get-current-userprintsinspectPhysicalof a serial in stateunauthorizedis refuseduntrusted, and runs no command naming itinspectPhysicalof a serial only on a network transport (192.168.1.5:5555, an mDNS_adb-tls-connect._tcpname) is refusednetwork-onlywith "only USB-connected devices can be enrolled", and runs no command naming itinspectPhysicalofemulator-5554listed on the default server is refusednetwork-onlywith "only USB-connected devices can be enrolled", and runs no command naming it- the transport decides, not the shape: a serial shaped like
host:porton a USB transport is inspected, and a USB-shaped serial on a network line is refused inspectPhysicalof a serial not listed, or listedoffline, is refusednot-connectedinspectPhysicalwhoseadb devices -lfails or passes 30 s, or whosegetprop, user id or package read fails after the device was found, is refusedunreadablenaming the read, writes nothing, and does not rejectinspectPhysicalwhoseam get-current-userprints0; rm -r /sdcard, nothing, or1234567is refusedunreadable, and runs nopm list packageslistPresentPhysicalanswers a given serial on USB in statedevicepresent with its API level, an unauthorized one not presentuntrusted, and an offline, network-only or missing one not presentnot-presentlistPresentPhysicalgives no entry, and runs no command, for a USB phone that is not among the given serialslistPresentPhysicalanswers every given serial not presentnot-present, and does not reject, whenadb devices -lfails or has no exit codelistPresentPhysicalanswers a present serial whosegetpropfails not presentnot-presentreclaimuninstalls, for the current user, each package off the enrollment list, uninstalls no listed package, reports listed packages not installed asmissingApps, and returns strategyuninstallreclaimwith nothing off the list runs no uninstallreclaimwhoseam get-current-userprints0; rm -r /sdcard, nothing, or1234567rejects, and runs nopm list packagesand nopm uninstallreclaimwhose user id read fails, or passes 30 s, rejects, and runs nopm list packagesand nopm uninstallreclaimwhose firstpm list packages -3fails, passes 30 s, or has a bad line rejects, runs no uninstall, and never resolves with an empty listreclaimrejects, and runs no later uninstall, when an uninstall printsFailure […], exits non-zero, or has no exit codereclaimrejects when the second listing fails, passes 30 s, or has a bad linereclaimrejects when the second listing still holds a package off the enrollment list- a package line with a character outside letters, digits,
_and., and a barepackage:, each fail the listing, and no uninstall runs - a package listing with
\r\nline ends and blank lines reads the same packages as one with\nand none - reads pass a 30 000 ms limit and uninstalls a 60 000 ms limit to the process runner
- while a physical
adb devices -lhangs,listManagedfor emulators resolves - while an emulator call holds
#withDeviceLock(index.ts:2041) and aPortAllocatorport (index.ts:2068),listPresentPhysicalandpassthroughon a physical device resolve passthrough("adb", <physical device>, ["shell", "getprop"])resolves toadb -P 5037 -s <serial> shell getpropwithANDROID_ADB_SERVER_PORT=5037passthroughon a physical device refuses a caller argument before the subcommand (-s,-t,-d,-e,-P,-H,-L,--one-device,--reply-fd), each ofkill-server,start-server,server,nodaemon,connect,disconnect,pair,reconnect,tcpip,usb,detachandreboot-bootloaderas the subcommand,rebootfollowed by each ofbootloader,sideload,sideload-auto-reboot,recovery,fastboot, and bareshellwith no terminalpassthroughon a physical device runsshell dumpsys usb,shell echo connectandrebootwith no argument: a refused word after the subcommand is an operand, and a plainrebootis not refusedpassthroughon a physical device is refused withPassthroughRefusedErrorwhen its check sees another version (the message names both), or cannot read a version (the message names which), and the process runner starts nothingpassthroughon a physical device returnsunsetEnvANDROID_ADB_SERVER_ADDRESS,ADB_SERVER_SOCKETandANDROID_SERIAL; with no device it returns none- (dispatcher,
src/daemon/dispatcher.test.ts)device.execwith anexecEnvholding a key the command'sunsetEnvnames spawns without that key, and keeps every other key passthroughwith no device still runs on Simlock's own server (today'sindex.test.tspassthrough tests, unchanged and green)passthroughwith no device, or with an emulator device, sends nohost:versionestimateof a reclaim for a physical spec is 90 000 ms at both clean levels; for an emulator spec it is unchanged- (phone lane)
simlock device add --platform android $SIMLOCK_PHYSICAL_ANDROIDenrolls it with the model and API leveladb shell getpropreports and as many apps aspm list packages -3lists for the current user - (phone lane)
simlock lease --platform android --physicalgrants that serial withANDROID_SERIALset to it andANDROID_ADB_SERVER_PORT=5037, anddevice.execadb shell getprop ro.serialnoover HTTP prints the serial - (phone lane) after installing
$SIMLOCK_PHYSICAL_ANDROID_APKwith plainadb -sand releasing, the device isready, the APK's package is gone, and every enrolled package is still installed - (phone lane) the plugged-in phone stays
readyacross three probe intervals - (phone lane) with a short
exec.timeoutMs(configOverrides), adevice.execadb shell sleep 60on the leased phone endsEXEC_TIMEOUT, and the adb server listening on 5037 still runs with the pid it had before the command - (phone lane, manual file) with a short
health.probeIntervalMs, the phone unplugged when the test asks in its log becomesabsentwithabsentReasonnot-presentwithin 150 s, and plugged back becomesready - (phone lane)
scripts/slow-e2e.sh --physical e2e/slow-android-physical.test.tsandscripts/slow-e2e.sh --physical-manual e2e/slow-android-physical-manual.test.ts, each run withSIMLOCK_PHYSICAL_ANDROIDunset, skip, and the skipped title names it; the same withSIMLOCK_PHYSICAL_ANDROID_APKunset
Done when
- With a default adb server of another protocol version, the Android driver runs no command on a physical device,
device addis refusednot-connectednaming both versions,device.execon a physical lease is refused naming both versions, andadvisories()names both versions even with no Android device enrolled (driver tests above). - A check runs right before every physical adb command, not once per call: a server that changes protocol between two commands of one call gets no later command (driver tests above).
- No physical call stops or starts the default server, and
dispose()leaves it (driver tests above). - A hung phone call does not delay an emulator call (driver test above).
reclaimwith a bad or unreadable user id, or a failed package listing, rejects and uninstalls nothing (driver tests above).- On a Mac with an Android SDK, one Android phone on USB with USB debugging authorized,
SIMLOCK_PHYSICAL_ANDROIDset to its serial andSIMLOCK_PHYSICAL_ANDROID_APKto an APK:device addenrolls it, a--physicallease gets it withANDROID_SERIAL,device.execreaches it, after release the installed app is gone and every enrolled app remains, and a timed-outdevice.execleaves the adb server on 5037 running with the same pid. Proved unattended byscripts/slow-e2e.sh --physical e2e/slow-android-physical.test.ts. This needs real hardware. - On the same machine, with a person at the phone, the phone unplugged on the test's prompt becomes
absent (not-present), and plugged back becomesready. Proved byscripts/slow-e2e.sh --physical-manual e2e/slow-android-physical-manual.test.tswithSIMLOCK_PHYSICAL_PERSON=1. This needs real hardware and a person; the hardware verifier reports itunavailable (needs a person), and the maintainer runs it. scripts/slow-e2e.sh --physical e2e/slow-android-physical.test.tsandscripts/slow-e2e.sh --physical-manual e2e/slow-android-physical-manual.test.ts, each run withSIMLOCK_PHYSICAL_ANDROIDorSIMLOCK_PHYSICAL_ANDROID_APKunset, skip, and the skipped title names the unset variable. This needs no phone.- A timed-out or failed
host:version, or a binary update to another adb protocol, blocks physical commands, and a daemon env'sANDROID_ADB_SERVER_ADDRESSnever reaches a physical command (driver and dispatcher tests above). docs/CLI.mdlists the Android physical-device prerequisites and theadb-protocol-mismatchadvisory;toolchain.mdhas the Android phone row, with the person check on the manual file;KNOWN-PITFALLS.mdhas the entry above.
Out of scope
- Routing physical requests and
device.execthrough a gateway (task 6). - Wi-Fi debugging, work profiles and other Android users.
- Restarting or fixing a default server of another protocol version.
- Guarding against another adb client restarting the default server.
- The iOS physical handler (task 4).
Depends on
- #435
- #440
Approval
- Approved for delivery
Written by an agent.
- Lingua principale
- TypeScript
- Stelle
- 19
- Fork
- 1
- Merge medio
- 6h 14m
- PR unite (30g)
- 141
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di callstackincubator/simlock
-
bug:new
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 84/100
callstackincubator/simlock#451 ·
I maintainer di solito rispondono entro 1 giorno
-
bug:new
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
callstackincubator/simlock#450 ·
I maintainer di solito rispondono entro 1 giorno
-
Lint does not enforce index.ts boundaries for cleanup/, routing/, warm-pool/ and contract/ helpersApertabug:new
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
callstackincubator/simlock#445 ·
I maintainer di solito rispondono entro 1 giorno
-
bug:new
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 70/100
callstackincubator/simlock#430 ·
I maintainer di solito rispondono entro 1 giorno
-
bug:new
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
callstackincubator/simlock#424 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di callstackincubator/simlock
Issue simili
-
bug cli service
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
create-element: same editorAlias silent-fallback bug as #201, not covered by that fixForse già presa Una pull request collegata a questa issue è aperta o già unita. Apertagenerated-by-ai
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
umbraco/Umbraco-CMS-MCP-Editor#208 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Table: Space fires onActivate in single-selection mode — the reference doc and the JSDoc disagreeAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
sidorares/react-x11-components#764 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
backnotprop/plannotator#1840 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
JoviDeCroock/pracht#432 ·
I maintainer di solito rispondono entro 1 giorno