SpringBoard domain: fold policy, window scenes, rotation, power
Simulator duo-baseline-springboard (048B820B-86E8-43F9-9DF1-F76802AD6DB8), iPhone19,4,
iOS 27.1 24A94425. Data: springboard.json (37408 records from 45 steps, 734 distinct
api keys). Raw transcripts are in raw/springboard/steps/, posture artifacts and screenshots
in raw/springboard/posture/, and class dumps in raw/springboard/classdump*.txt.
How it was recorded
probes/springboard-state.lldbis one expression that walks from-[SpringBoard _displayToolService]to the content-mode coordinator, resolver, window-scene manager, pose provider and swap coordinator, and from there to eachSBWindowSceneand its controllers. It records every getter it lists, and every declared property ofSBWindowSceneand of the settings classes (enumerated withclass_copyPropertyList). Getters run throughNSInvocationafter arespondsToSelector:check, because lldb expressions cannot catch exceptions (@tryis rejected). Ivars are read by their type encoding.probes/springboard-run.zsh <step> <posture args|status>runstools/posture.zsh(withPOSTURE_OUT=raw/springboard/posture) and then the state probe.probes/springboard-merge.zshbuildsspringboard.json; its step table is the run order, except that the two*-initial/*-unfoldSafari steps were captured again after the lock tests. The first captures were overwritten.- API naming:
SBWindowScene[cover]means surfaceType 1 (SB-main, displayMain).SBWindowScene[inner]means surfaceType 2 (com.apple.springboard.bringup,Unknown.main[3]).SBFDisplayProxy[cover|inner]follows the same rule. Object-valuedSBWindowSceneproperties are recorded as<ClassName>(presence and class), not as their description. Addresses are normalised to0x*in the JSON. orientationis the primary scene'sUIWindowScene.interfaceOrientation, as in smoke.json. The requested device orientation and any foreground app are in theposturelabel, for exampleclosed[device:landscapeLeft,app:MobileSafari].- Enum values: content mode 1 = disabled, 2 = primaryContent, 3 = auxiliaryContent. Resolver
_preferredTransitionStyle1 =.fade, 2 =.continuousLive, 3 =.continuousSnapshot.SBBacklightController.backlightState1 = on, 3 = always-on (AOD;screenIsOnNO, lock screen visible and dimmed), 4 = off.
Results
These rows hold for every orientation and app tried. activeWindowScene, the active
lock-screen manager (SBDisplayContentModeLockScreenActiveDisplayPolicy), alert items and
keyboard focus all follow the primary scene. [SBLockScreenManager sharedInstance],
[SBIconController sharedInstance] and +[SBBacklightController sharedInstanceForMainDisplay] always belong to the cover scene. Each scene has its own
home-screen controller, cover sheet window and status-bar window, and each of them sits in
its own scene.
Rotation
The swap tool's -deviceOrientation= reaches the pose. SBFDevicePoseProvider stores the
device orientation together with a per-panel orientation: device landscapeLeft (3) gives cover
4 and inner 3, landscapeRight (4) gives cover 3 and inner 4, and portraitUpsideDown (2) gives
2 for both. UIDevice.orientation in SpringBoard follows the cover orientation.
SBFDevicePoseProvider->_accelerometer is nil in the sim.
- Cover: home screen refuses landscape; a landscape-capable app gets it.
- Home screen: the cover's
SBTraitsParticipantRoleHomeScreensupports only portrait (mask 2). The cover'ssystemArbitratedDeviceOrientationbecomes 4, 3 or 2, butsystemArbitratedInterfaceOrientationstays 1. - MobileSafari in front: its participant (
SwitcherLiveOverlay, mask 26) rotates. Device landscapeLeft gives cover interface 4 and landscapeRight gives 3.[UIScreen mainScreen].boundsbecomes 678×466. The display 1 capture is 2034×1398 and shows Safari laid out in landscape (raw/springboard/posture/20261029-143539-closed-d1.png). - SpringBoard's own
SBWindowScene[cover].interfaceOrientationand the cover status bar stay 1 throughout. - So "cover refuses landscape" matches stock behaviour only while the home screen (or another portrait-only participant) is in front. It is not a rule for the panel.
- Home screen: the cover's
- Inner: accepts all four orientations, home screen and apps alike (HomeScreen mask 30).
- The mapping is rotated by 90°: device portrait gives interface 4, landscapeLeft gives 1, landscapeRight gives 2 and portraitUpsideDown gives 3.
- Inner
UIScreenbounds follow the arbitrated orientation: 951×669 for interface 3 or 4, 669×951 for 1 or 2. coordinateSpace.boundsof the innerSBWindowScenestays 669×951.
- Orientation lock: the cover's
SBWindowSceneOrientationLockManagerdelegates to the globalSBOrientationLockManager(_isPerDisplayLockEnabledNO). The inner has a per-display lock (_isPerDisplayLockEnabledYES). Neither was locked during the run. - While a panel is dark, its pipeline holds a "Backlight off" device-orientation deferral, so
that panel's
systemArbitratedDeviceOrientationgoes stale. An example is the cover reading 2 while the inner is open in portrait. SBTraitsContinuousFoldDeviceOrientationLockAssertionwas never held at rest:_continuousFoldLockValidator,_continuousFoldDeviceOrientationDeferralAssertionand_settleOrientationLockwere nil and_continuousFoldHolderCountwas 0 in every step. It exists only during a sweep, and no probe ran mid-sweep.
History-dependent values
- Inner
SBWindowScene.interfaceOrientation(andeffectiveGeometry) is 1 from boot until the first rotation that reaches the inner panel, even though SpringBoard arbitrates 4 for it and innerUIScreenbounds are already 951×669 after the first unfold. After any inner rotation it tracks the arbitrated value (4 in portrait). This was confirmed on a second boot (open-boot2gives 1;open-boot2-landscapeLeftthenopen-boot2-portraitgives 4). It is not a SpringBoard-relaunch effect. - Inner
UIScreen.boundsis 669×951 at boot, before the inner has been lit. After that it follows the inner's arbitrated interface orientation (see above). The README's "951×669 after the first unfold" is the portrait case of this. - Resolver
_preferredTransitionStyleis 1 (.fade) at boot until the first swap, then 2 (.continuousLive). It is 3 (.continuousSnapshot) while MobileSafari is in front. SBDisplayToolServiceCoordinator._currentDirection,continuousFoldController._lastHingeAngleDegreesand_lastContinuousFoldPosition, and the resolution's storeddevicePose(for example "pose: 100.0°" while open) keep the values of the last sweep. Compare them by path, not by posture.coverSheetWindowIfLoadedis nil on both scenes until the first swap, and loaded and hidden afterwards.- The traits-arbiter participant count varies with history: 25–29 per scene. Duplicate
CoverSheetGrabberparticipants accumulate over book sweeps.
Power and lock
- In the sim, auto-lock is off:
SBIdleTimerGlobalCoordinatorholds the disable assertion "SBDisableAutoDim user default.",idleTimerisSBDisabledIdleTimer, and the enabled descriptor is Auto, warn 40 s, total 60 s.probes/springboard-enable-autolock.lldbinvalidates the assertion, which arms the real idle timer.probes/springboard-disable-autolock.lldbtakes the assertion again. - When auto-lock fires in open or book, the inner goes to AOD (state 3) and stays primary. The cover stays off.
- Folding to closed while auto-locked lights the cover, not the inner.
- The policy becomes FullyClosed, the cover is primary in AOD (state 3, lock screen dimmed) and the inner is off (state 4, display 3 captures black).
- This holds both from open and from book.
- The VM's "closing after auto-lock lights the folded inner display" does not happen in the sim.
- Folding shortly after a lock-button press from open (rather than auto-lock) turned the cover fully on (state 1, lock screen).
_simulateHomeButtonPressWithCompletion:does nothing on this device._simulateLockButtonPresswakes it.attemptUnlockWithPasscode:nil finishUIUnlock:YESunlocks it only while the idle timer is not expired.
Missing, nil, errors
SBWindowScene.liftToWakeControllercrashes the probe expression (EXC_BAD_ACCESS on the stack) and is recorded as an error, not called.SBFDevicePoseProvider->_accelerometer(BKSAccelerometer) is nil.- Nil in every step:
- Transition state:
currentTransitionContext(coordinator and resolver), resolver_currentTransitionCoordinator,displayContentModeContext.swapTargetand_currentContext. - Unused orientation and lock state:
overrideActiveLockScreenManager, the continuous-fold lock state listed above, and theSBContinuousFoldCoordinatorand hinge-monitor assertions. SBWindowSceneManager._continuityDisplayWindowSceneand_extendedDisplayWindowScene.SBWindowSceneproperties:commandTabController,hardwareButtonBezelEffectsCoordinator,interactiveScreenshotGestureManager,manualActivationShieldWindow,medusaHostedKeyboardWindowController,menuBarManager,modalLibraryController,phoneRLControllerandreachabilityController.
- Transition state:
Incidents and tool notes
- SpringBoard crashed once (pid 50307,
raw/springboard/crash/). It hit SIGSEGV in-[_SBSystemApertureContainerViewRotatingContentView setTransform:], about 18 s into an inner landscapeLeft → landscapeRight rotation.posture.zsh's readback probe had stopped SpringBoard during the rotation animation. That step was discarded (raw/springboard/failed-steps/). Repeating the same rotation withPOSTURE_SETTLE=25(no probe during the animation) did not crash, so later orientation steps usedPOSTURE_SETTLE=15–25. Steps after the crash are labelled "after SpringBoard relaunch". - The
posture.zshdefaultPOSTURE_SETTLE=3lets the readback probe stop SpringBoard mid-animation. This is harmless for posture swaps but caused the crash above during a 180° inner rotation. - One stray
posture.zsh statusrun of mine wrote to the sharedraw/posture/. It was moved toraw/springboard/posture/misplaced/. closedafterbookstopped at 3.6° in the resolution's stored pose, while the pose provider read 0.