Turning the screen on and unlocking
vphoned's screen.unlock (VPhoneDaemon/Daemon/GuestScreenUnlock.swift,
VPhoneDaemon/Native/vphoned_unlock.m) and vphone-launchpad-cli guest unlock.
Measured on 2026-10-04 against unlocktest-iphone (iPhone17,3, iOS 27.0
24A435, no passcode), with the 26.6.2 dyld shared cache for the static read.
Why a method
A guest is at the Lock Screen after every boot, every SpringBoard restart and
every press of the side button, and it is dark a few seconds later
(lock_screen_idle_timer.md). A locked or dark screen swallows taps, and
apps.launch answers device_locked.
The hardware keys only toggle. power wakes a dark guest and darkens a lit
one; home wakes, and on a lit Lock Screen without a passcode it dismisses to
the Home Screen. A script has to read device.screen first and choose. The
method does that reading and does only what each state needs.
What SpringBoard's own unlock request does, and why it is not used
SpringBoardServices has -[SBSLockScreenService requestPasscodeUnlockUIWithOptions:withCompletion:] (0x191f505c8 in the
26.6.2 cache), the request an app makes when it needs an unlocked device.
Served to a caller with com.apple.springboard.requestDeviceUnlock (the
authenticator in -[SBLockScreenService init] is built with that entitlement
string, at 0x224be0ae4).
Granting vphoned that entitlement and calling the request was tried first. The guest log shows why it does not unlock a passcode-free device:
Asked while the screen is off, SpringBoard treats the request as turn the
screen on first: it lights the backlight and bails out of the unlock
(autoUnlock = 0), leaving the Lock Screen up. A second request, with the
screen now on, did not dismiss it either. The request is the wrong tool for a
passcode-free guest, and it needs a private entitlement. Both were dropped.
What screen.unlock does
- Reads
com.apple.springboard.lockstateandcom.apple.springboard.hasBlankedScreen(IcliKit'slockState()). Untilcom.apple.springboard.finishedstartupis non-zero these read "unlocked, lit" no matter what will show, so at startup it waits for that notification before trusting them. - Not locked, only dark:
SBSUndimScreen, and done. This lights the display with no toggle and needs no entitlement (it is what icli'swakecalls). - Locked: if dark,
SBSUndimScreenfirst so the next step lands on a live Lock Screen. Then press Home.- No passcode: Home dismisses the Lock Screen. Repeated until unlocked or the deadline, in case the first press landed while the display was still coming up.
- Passcode: Home raises the passcode pad;
passcodeis typed (digits as keyboard number keys0x1E–0x27, anything else as text), with Return if the pad has not submitted after a second.
- Polls the two notify states every 100 ms until lit and (if it was locked)
unlocked, or
timeout(10 s by default, 1–60) runs out.
Result: {locked, screen_off, was_locked, was_screen_off}; capability
screen_unlock. No private entitlement.
Live results (no-passcode guest)
All on unlocktest-iphone, each followed by device.screen and
apps.foreground:
So the device is actually usable afterwards, not merely reporting unlocked.
Unlock at startup
A machine whose config.plist has unlocksAtStartup (vm config <name> --unlock-at-startup on, Device > Unlock at Startup, or Launchpad's machine
Settings) has vphone-vm call screen.unlock with a 60 s timeout when a
vphoned that has just started connects. vphoned's /v1/health carries
instance, a UUID made when it starts; the host keeps the last one it saw
across disconnects, so a probe that drops and reconnects to the same vphoned
is not unlocked again. A guest still in Setup Assistant is skipped. A vphone guest cannot have a
passcode (see "Why a passcode cannot be set" below), so the setting never
meets one.
Measured on 2026-10-04 on autounlock-iphone (iPhone17,3, iOS 27.0 24A435,
no passcode, Auto-Lock 1 minute), bundle 2.6.0-local:
The re-lock after a minute is the guest's own Auto-Lock acting on an unlocked device, not a failed unlock. The very first reading after a boot can say "unlocked, lit" before SpringBoard has started (see step 1 above), so a check has to watch for a while, not trust one sample.
On a second guest (unlockpr-iphone, same build, 2026-10-05) one start with
the setting on stayed locked: the log read [unlock] the guest was already unlocked. com.apple.springboard.finishedstartup was already set, yet the
two notify states still read unlocked, and the Lock Screen came up a few
seconds after screen.unlock had returned. So the host does not take
"already unlocked" at startup as final: it polls device.screen once a
second and calls screen.unlock again when the Lock Screen appears, for up to
90 s after the connect (VPhoneGuestControl.unlockAtStartup). With that:
Device › Unlock at Startup in the VM window checked itself, wrote
unlocksAtStartup = true to config.plist at once, left the running guest
locked, and the next start unlocked it.
Setting a passcode on unlockpr-iphone through Settings did not work. The
new-passcode pad there takes four digits (its field reads "0 of 4 values
entered"); a first try with six digits spilled two into the verify step and
ended in "Passcodes Did Not Match". With four digits, entered a digit every
1.5–2 s and checked after each, both entries matched, and Settings then
answered "Passcode Change Failed" (two different codes, same result). The
passcode path therefore stays unmeasured.
Why a passcode cannot be set (2026-10-05)
The same failure is upstream issue #467 ("无法设置锁屏密码"), closed without
a cause. Logs captured per process with logs.syslog {process} during one
attempt show where the change stops:
- Preferences → ManagedConfiguration:
Change passcode with context error … The passcode cannot be set (-1),MCPasscodeErrorDomain5014. - profiled:
com.apple.Preferences is attempting to change the passcode, then callscom.apple.mobile.keybagd.xpcand logsFailed to set new passcode. Result: -1. - keybagd:
KBChangeSystemSecret: handle: -3, se-support: 0, primary-user: 1 …,KBisxARTBasedKeyBag: Result = 1,KBChangeSystemNonSeSecret: change-secret failed e00002ce. - kernel:
"AppleSEPKeyStore":pid:56 … operation failed (sel: 15 ret: e00002ce)(pid 56 is keybagd).e00002ceiskIOReturnNotReadable, and the kext returns it as the SEP's answer, so the SEP refuses the change.
What differs from a real iPhone, from the same boot's console log:
MKB_INIT: No system keybag found on filesystemandNo system keybag loaded./private/var/keybagsholdsusersession.kbandpersona.kb, nosystembag.kb. Boot continues only because the vphone600 device tree sets MKBdt = 1(seeResearch/Firmware/firmware_manifest_and_origins.md).no-effaceable-storage is ON, and AppleSEPKeyStore logsdisabling use of effaceable storage, using fake key, 0xffffffff. The switch is/defaults/no-effaceable-storageinDeviceTree.vphone600ap, which a phone's device tree does not have; the VM has no effaceable storage.- The SEP firmware is
sep-firmware.vphone600.RELEASEfrom the cloudOS (PCC) IPSW. PCC nodes have no user passcode.
So the refusal comes from the SEP keystore on a platform built without
effaceable storage or a system keybag, not from vphoned, the input, or the
UI. Nothing on the host or in the guest's userland changes that. Making it
work would mean changing how the guest's keystore is provisioned at restore
and boot, which this note does not attempt. Until then a passcode guest
cannot be made, and screen.unlock's passcode path cannot be run.
Tap to wake
A phone's touch controller reports a tap on a dark screen as a wake gesture, and SpringBoard lights the Lock Screen for it. The VM's touchscreen has no such gesture, so a click in the window on a dark guest did nothing.
vphoned (vp_tap_to_wake_start in vphoned_unlock.m,
GuestScreenUnlock.startTapToWake) watches
com.apple.springboard.hasBlankedScreen. While it is set, an
IOHIDEventSystemClient of the monitor type (vphoned has
com.apple.private.hid.client.event-monitor) sees every HID event, and a
digitizer event with kIOHIDEventFieldDigitizerTouch set (at most one every
half second) wakes the screen:
SBSUndimScreen.- If the screen is still dark 0.5 s later, one side-button press
(consumer
0x30), at most one every 1.5 s so two never make a Wallet double-click. On a dark screen the side button only wakes it.
Home is never pressed, so the guest stays on its Lock Screen. The client is
released when the notify state clears. It works for every touch source the
guest's HID system sees: the VM window, a Launchpad viewer, input.tap.
backboardd itself drops the waking touch (ignoring touch event ((backlight dim/off))), so the tap never reaches what is under it.
On iOS 27.1 (24A94425, waketest-iphone, 2026-10-29) SpringBoard refuses
SBSUndimScreen from vphoned: _SBXXDimScreen Authentication failed: Process lacks credentials to access this resource. So step 2 is what lights
the screen there, and on that build screen.unlock and icli's wake light
it only through their Home-press fallback. Which entitlement the MIG
handler checks has not been looked up.
The Controls panel's Wake button is different: it is icli's wake, which
presses Home after SBSUndimScreen until a passcode-free guest is unlocked.
Not yet measured
- Passcode guests. vphone does not support a passcode (see "Why a passcode cannot be set" above), so the Home-raises-the-pad-then-type path cannot be run. It is written to the behavior of a real device.
- iOS 27.0.1 (its guests kernel-panic at first boot, separate issue).
Reproducing
The Bailing from UIUnlock evidence, if the request path is revisited: