系统桌面:适配 OS4 底栏柔光玻璃,并修复 OS4 下底栏修改失效问题,适配部分布局功能 - #1686
Open
LDS-XiaoYe wants to merge 50 commits into
Open
LDS-XiaoYe wants to merge 50 commits into
LDS-XiaoYe wants to merge 50 commits into
Conversation
Render Dock glass with an app-owned HWUI host and Control Center preset. Add bounded startup recovery, scoped persistent diagnostics, OS4 style migration, and host regression tests. Document the unresolved vendor recents observer endpoint and pending cold-boot verification.
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| BestPractice | 1 medium 2 minor |
| Documentation | 1 minor |
| ErrorProne | 13 high |
| Security | 50 critical 12 high |
| CodeStyle | 14 minor |
| Complexity | 7 medium |
🟢 Metrics 7541 complexity · 82 duplication
Metric Results Complexity 7541 Duplication 82
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
LDS-XiaoYe
force-pushed
the
os4-branch
branch
from
September 4, 2026 16:34
01dcd9c to
8eb09d0
Compare
- Add DockGlassProcessGuard to prevent renderer process frozen by GreezeManagerService - Add DockGlassProcessPolicy for UID/PID ownership tracking - Add DockGlassSurfaceLease for serialized surface lifecycle on IPC worker - Refactor DockGlassClient to use lease-based attach/detach/release - Update HomeDockWindow with process guard integration - Add DockGlassProcessPolicyTest and DockGlassSurfaceLeaseTest - Update README with diagnostic version 11 surface lifetime docs
… OS4 dock glass - Add dynamic ARM64 code shape resolver for launcher motion callbacks - Add DockGlassProcessGuard to prevent renderer process frozen by GreezeManagerService - Add DockGlassProcessPolicy for UID/PID ownership tracking - Add DockGlassSurfaceLease for serialized surface lifecycle on IPC worker - Refactor DockGlassClient to use lease-based attach/detach/release - Update HomeDockWindow with process guard integration - Sync upstream: fix(wifi) improve null checks in WifiStandard hooks - Add DockGlassProcessPolicyTest and DockGlassSurfaceLeaseTest - Update README with diagnostic version 11 surface lifetime docs
- Add [[maybe_unused]] to unhook_func in hyperceiler_home.cpp - Catch specific exceptions in DockGlassClient and DockGlassProcessGuard - Simplify complex condition in DockGlassClient - Move fields before interface in DockGlassSurfaceLease - Add @SuppressWarnings for intentional reference equality in DockWallpaperEndpoint - Extract repeated 'system' string to LOG_TAG constant in HomeDockWindow - Add pragma to suppress unused member warnings in test files
- Catch specific exceptions (IllegalStateException, RemoteException, SecurityException) in DockGlassClient - Add @SuppressWarnings for intentional reference equality in DockWallpaperEndpoint - Extract duplicate 'close'/'release' strings to constants in HomeDockWindow - Add proper buffer boundary check with exception in DockNativeResolverTest
- Remove non-functional DockAutoHideOS4 hook (OS4 Flutter has no Java folder/editPanel methods) - Add auto-hide logic directly in HomeDockWindow using overview state detection - Update autoHideState in updateOverview when entering/exiting recents - Fix DockWallpaperEndpoint duplicate SuppressWarnings annotation - Add missing RemoteException import in DockGlassClient The dock background now automatically hides when entering recents (overview=true) and shows when returning to home (overview=false) via wallpaper animation commands.
- Remove autoHideState mechanism (not needed for OS4 Flutter launcher)
- Remove recents overview auto-hide code
- Simplify visibility to just use window.callMethod("isVisible")
- OS4 Flutter launcher handles drawer/edit mode internally
Note: Drawer and edit mode auto-hide cannot be implemented via Java hooks
because OS4 Flutter launcher doesn't expose these states through Java methods.
The Flutter launcher renders these UI elements within its own view.
- Migrate native motion from socket to authenticated IWindowManager Binder - Add DockNativeMotionEndpoint for receiving motion samples via Binder - Eager identity binding to prevent early sample loss - Deferred wallpaper editMode detection (setTo-only entry, 500ms confirm) - Native scene change clears editMode on recents/home-return - Remove conflicting direct transaction from setEditMode - Preserve native motion state across editMode toggles - EditState=7 (shortcutMenu) now hides dock glass - All tests passing (DockNativeMotion, DockRecentsMotion, etc.)
junhaoyyds
added a commit
to junhaoyyds/HyperCeiler
that referenced
this pull request
Sep 10, 2026
… into main - DockGlass feature + native cmake build for libhyperceiler_home.so - drop prebuilt jniLibs copy (built from app/src/main/cpp now)
Native motion sampling from com.miui.home was silently dying at several points in the chain. Fixed the whole path end to end: - Mapping discovery: libapp.so is mapped straight out of base.apk (extractNativeLibs=false), so the old "path ends with /libapp.so" filter matched nothing and no hook bank was ever installed. Added three-layer container discovery (raw libapp.so / ZIP-stored entry / page-aligned ELF header) with attribution by ZIP view offset, plus the new dock_native_runtime.h and its host tests. - Patch loss self-heal: the AOT code pages are file backed, so the kernel can discard and re-read them from the APK, silently reverting the inline prologues. install_slot()/add_instance() made that unrecoverable; added ensure_slots_live() re-arming, restore_patch_words() fallback, and a 10s "motion pipeline" heartbeat as the unconditional truth source. - Transport reuse on resume: suspend/resume used to tear the Binder transport down and rebuild it (0.36-3.66s with no samples), covering the "swipe right after unlock" window. It is now reused and re-sent immediately. - Binder identity binding: bindIdentity() was only called when uid/pid changed, so a fresh receiver after a module hot reload retained-and-dropped every sample (retained sample until exact launcher PID binds). It is now re-asserted on every traversal, with a 1s scheduleNativeBindSweep() fallback. - DockDiagnosticJournal now reserves ring-buffer room for one-shot lifecycle events instead of letting per-frame positions evict them. Also remove the unused FolderGlassOS4 rule: it was never imported or initHook'd in HomePhone/HomePad, and no reflection-based registration exists.
…er glass native: a trampoline whose continuation pointer went missing left `br x16` jumping to address 0, crashing com.miui.home inside ShortcutSceneAnimator._handleShortcutSignal with pc=0 / x16=0. install_slot, restore_patch_words, ensure_slots_live and bank_healthy now verify the continuation, restore the untouched prologue and refuse the slot instead of keeping an unconditional jump to null; the pipeline heartbeat gains a guard counter so a refused slot stays visible after logcat wraps. glass: the Dock carried the control-center card token (dual blur 110/110) and picked it from the UI night mode. The launcher keys its folder-icon glass off the wallpaper brightness instead (hasAppliedLightWallpaper), so the Dock now carries the Medium_Thin_Low/High folder token with dual blur 36/500 and resolves the brightness on every apply. probe() reports not-ready when the resolved brightness differs from the applied material, which routes the launcher through refresh() so a wallpaper change is followed.
Move the OS4-only home entry points out of the version-first `os4/`
folders so the version tag lives in the class name instead, matching
the repo convention:
- app/os4/home/{HomePad,HomePhone}.java -> app/Home/os4/
(package ...libhook.app.Home.os4)
- rules/home/os4/HomeSettingsOS4.kt -> rules/home/other/
- rules/home/os4/NativeHomeHooks.kt -> rules/home/other/NativeHomeHooksOS4.kt
NativeHomeHooks is renamed to NativeHomeHooksOS4 because its two
`external fun`s are resolved by their JNI symbol names, which encode the
fully-qualified class name (there is no RegisterNatives table to keep
the two in sync). The matching exports in
app/src/main/cpp/hyperceiler_home.cpp are updated in the same commit.
Also drop the dead stub rules/home/dock/DockAutoHideOS4.kt (0
references) and harmonize the touched files' license headers with the
repo's dominant full-AGPL header.
Replace the one-line SPDX short headers on the fork-owned OS4 dock glass / native motion files with the repo's dominant full-AGPL header, so the header style no longer depends on when a file happened to be added: - rules/home/dock/: DockGlassClient, HomeDockWindow, DockGlassProcessGuard, DockGlassProcessPolicy, DockWallpaperEndpoint, DockNativeMotion, DockGlassPreset, DockNativeMotionEndpoint, DockRecentsMotion, DockGlassSurfaceLease, DockGlassRetryPolicy, DockWindowPolicy - provider/: DockGlassDiagnostics, DockGlassHost, DockDiagnosticJournal Kotlin files take the 18-line header with ` *` indentation; Java files take the blank-line variant (490 vs 154 across the repo, and the sibling DockCustom.java / FoldDock.java both use it). MiBlurView.kt deliberately keeps its original header: it is an upstream file carrying third-party GPL-3.0 terms and authorship (XiaomiHelper / HowieHChen), so rewriting it would misstate its licensing. Comment-only change: every added line is a license-header line, no package declaration or code line is touched, and the rebuilt release APK is unchanged.
Add DockUnlockReveal, an 821ms ease-out reveal (rise + fade, no scale - scaling resamples the glass and smears it) for the dock background, driven by KeyguardController.keyguardGoingAway(int). That call fires exactly once per real unlock and never during AOD/doze transitions, unlike setKeyguardShown(false), which lands ~315ms after the launcher's own _showPresent and also fires from updateDeferTransitionForAod. - reveal clock anchored to the transition start; the hidden first pose (alpha 0, rise offset) is committed inside the BEFORE hook, so the dock is never drawn at rest before it animates - traversal positions include the reveal rise, so the frame loop and WMS layout write the same value instead of fighting at display rate (previously visible as vibration right after the unlock) - temporary reveal dbg tracing (3s window, rate limited) kept for the ongoing post-unlock wallpaper-swap flash investigation; the block is marked TODO(twitch-diag) for removal - reveal state machine unit tests under tests/home-dock-window
…r swap With different keyguard and home wallpapers, the unlock transition swaps them, which flips the glass darkness probe mid-flight and tears the material down and back up - the flash of sampling two different wallpapers. Freeze for MATERIAL_SETTLE_MS (1.6s, the reveal plus a margin) after keyguardGoingAway, then allow exactly one rebuild: - updateGlass() keeps the existing ticket while the settle window is open, so the darkness flip cannot release and recreate the glass - resume() grows an allowFallback flag; while settling, an unhealthy probe only records, keeping the current native material on screen instead of flashing the compositor fallback and restarting the producer - when the window closes, a posted traversal applies the home wallpaper's darkness in one rebuild and re-probes with fallbacks enabled Also raise the reveal rise 22dp -> 34dp: the first frame is posed before the dock is drawn, so the larger offset cannot be seen as a jump, and the reference device read 22dp as too subtle.
junhaoyyds
added a commit
to junhaoyyds/HyperCeiler
that referenced
this pull request
Sep 11, 2026
…al, glass freeze, realtime follow) Syncs 6 new commits from LDS-XiaoYe/os4-branch up to 86b24e6. Desktop dock hook (libhyperceiler_home.so via source) and CI workflows preserved.
…time - DockGlass: resolve the renderer UID defensively so a package that is momentarily unresolvable (e.g. mid-upgrade) becomes a bounded, never-exhausting dependency retry instead of escaping into the system_server uncaught handler; every asynchronous entry point now sits behind an Exception-only guard, and the recoverable-failure helpers never swallow VM-fatal throwables - Recovery: extract the ticket state machine into DockGlassRecoveryGate (single-flight recovery, generation/readiness/refresh epochs, stale callback rejection, idempotent release/close) as an Android-free, host-testable implementation with 9 new test cases - Runtime: split the desktop native hook runtime into reusable layers under app/src/main/cpp/nativehook/ (ELF image, GOT backend, inline hook backend, ARM64 decode, page guard, mapping inventory); the dock maintenance pass now treats an unreadable mapping inventory as "unknown" instead of "gone", and clears a residue only once both the slots and the captured page protections are restored - Tests/CI: host test runner script, Gradle hostNativeTests task and a CI job for the host-side native suite Derived-file attribution is recorded in app/src/main/cpp/nativehook/THIRD_PARTY_NOTICES.md.
…le lib - Move the desktop-specific entry point, resolver, layout, hooks and motion sources into app/src/main/cpp/targets/home/, keeping nativehook/ as the target-agnostic hook infrastructure; pure moves, no content changes, git rename detection intact - CMakeLists: point target_sources at the new layout and add a root include directory so `nativehook/...` includes stay untouched - Rename the packaged library to libHyperCeilerNative.so: CMake target, the loadLibrary constants in NativeHomeHooksOS4, and the META-INF/xposed/native_init.list entry are updated together, since the framework matches the list by file name - Tests: update the relative include paths of the five host tests
ghhccghk
previously approved these changes
Sep 12, 2026
The lift now travels 96dp instead of 64, with the same single overshoot and zero-velocity landing. The dock still never scales its glass: a SurfaceControl matrix resamples the blur material and visibly smears it. The reveal also holds its start pose for a measured 10ms icon lead. Three unlocks on device showed the launcher's own _showPresent following our keyguardGoingAway epoch by 9-10ms, so the background used to leave half a frame before the dock icons. Wall-clock deadlines moved to the new TOTAL_MS (lead + 821ms) while DURATION_MS keeps describing only the curve. Version marker is now style=lift-settle-v3 with riseDp=96.0 on every transition-start, so a stale system_server can be told apart from a reloaded one. Tests cover the lead phase and the wider trajectory, and the timing assertions that hardcoded 821ms now derive from TOTAL_MS so changing the duration cannot silently invalidate them again.
Dock unlock entrance and background glass, accumulated: - reveal: replace "drop and bounce" with "orbital sweep". Two underdamped springs (lateral tuned faster) turn the rise into a shallow arc instead of a straight line; it starts 44dp low, 8% of the width to the left, -3.2deg and 0.86 scale, crosses the resting line once and lands normalized. - reveal: "arrive from depth" keeps the only view-side 3D projection, and it is applied inside the glass host's own process against its own Choreographer, so the perspective never runs on a system_server thread. - reveal: "contract and calm" now gathers from 0.84 rather than 1.1 - a child surface clips anything above its bounds, so the previous direction was effectively invisible. - glass: release a dead host ticket so the layer can be rebuilt, instead of being owned forever by a dead renderer (2s backoff keeps a crash loop from respawning on every traversal). - i18n: add the six unlock-style strings to the 14 translated locales (zh-rTW/zh-rHK keep traditional forms). zh-rME is the joke locale and is deliberately left alone. - tests: cover the arc lead, the normalized landing, the single overshoot, the monotonic gather and the reused early fade.
The entrance animation and the dock motion used to be driven by a Choreographer we created ourselves on WMS's handler thread - that is, on the android.display thread. That is what killed system_server: #00 libgui DisplayEventReceiver::getLatestVsyncEventData ReChronoRain#3 android.view.DisplayEventReceiver.getLatestVsyncEventData ReChronoRain#4 android.view.Choreographer$FrameData.update ReChronoRain#5 android.view.Choreographer.doFrame ReChronoRain#6 android.view.Choreographer$FrameDisplayEventReceiver.run fault addr 0x10 means mReceiverPtr == 0: the receiver had been torn down while the display looper still held a dispatched vsync callback. Retaining every clock reference forever only covered the GC -> finalizer path; the platform itself invalidates the receiver when the display configuration changes, and getInstanceForSurfaceControl(0L, looper) asked for a receiver with a null layer handle in the first place. Replace the whole mechanism with a self-re-arming Handler tick on the same WMS handler: one tick per displayed frame (8ms), System.nanoTime() as the time base, and the existing epoch / stall / backoff scaffolding kept intact. Nothing is created, retained or un-latched any more, so system_server never owns a DisplayEventReceiver and that SIGSEGV is structurally unreachable. Also drop setFrameTimelineVsync from this path: the vsync id came from the frame clock that is gone, and a stale id is worse than none - the static and traversal submissions apply without one and are just as correct. Verified on device: four dock layers present, motion pipeline healthy, and the reset history contains none of the old frame-clock records while the new motion records still appear.
The minus-one screen hides the launcher's own dock, which left our glass panel as the only thing floating over it. Fold that state into the visibility flag the appearance transaction already keys off, so the hide, the glass refresh pause and the frame loop stop all follow from one line. The overlay is detected in system_server by its window title: OS4's launcher is a Flutter application with no dex at all, so edit mode and the pages are Dart-side state that no Java hook can reach, while the overlay is a real WindowState that WMS owns. The visible case short-circuits on the cached state; otherwise the window map is re-scanned at most every 50ms, because a launcher restart replaces the WindowState.
junhaoyyds
added a commit
to junhaoyyds/HyperCeiler
that referenced
this pull request
Sep 14, 2026
…t, module lib rename) 10 new commits from LDS-XiaoYe:os4-branch up to 71c04fd. Resolved modify/delete conflict on .github/workflows/ci_build.yml by keeping it deleted (upstream version needs signing + Telegram secrets, unusable in fork). Fork CI (build.yml / nightly.yml) and desktop dock native_init.list preserved. Also drops the obsolete .github/scripts/align_final.py.
junhaoyyds
added a commit
to junhaoyyds/HyperCeiler
that referenced
this pull request
Sep 14, 2026
PR ReChronoRain#1686 (72d0155) referenced @string/home_is_rust_tips from library/core/src/main/res/xml/home_new.xml but only defined it in the app module, which AGP rejects (AAPT: resource string/home_is_rust_tips not found). Define it in library/core strings_app.xml (en + zh-rCN) so the fork builds. Upstream should fix this the same way.
… rotation The Dock panel could disappear entirely, and after leaving a rotated app the glass sampled the wrong background. Both came from the same place: the host's visibility and its background capture were each decided once, at a moment when neither could be known yet. Visibility - DockGlassSurfaceLease.attach() is once per generation, so the "stay hidden until the renderer reports a ready texture" write it carried could never be undone. The host stayed hidden while the tint was already cleared to alpha 0 and the blur to 0, leaving the panel with nothing to draw. Visibility now goes through an idempotent setVisible(), issued on every readiness change (readiness is part of the appearance key). Rotation - The gate used the launcher window's aspect ratio, which never reports a rotation here: the home screen stays a 1200x2670 portrait window while the display rotates around it, and Configuration.orientation flips on its own. DockRotationPolicy takes the display rotation instead and keeps sampling suspended for the whole rotated period. - Nothing observes the display while an app owns a rotated screen: WMS destroys the launcher window's surface and stops traversing it (the one-second sweep dies with the last layer), and the display listener does not deliver app-requested rotations on this ROM. The gate is therefore also carried by a hook on DisplayRotation.setRotation(I), checked against services.jar first. - A rotation never changes the launcher frame, so the glass key never changed and the host kept sampling the rotated background. The rotation epoch is part of the key now, so the settled portrait background is resampled. Capture timing - Returning to portrait resumes the retained capture from the transaction that commits the dock pose. That pose commits ~830 ms after the launcher starts its wallpaper zoom and the return motion only goes idle ~480 ms later, so the capture was restarting while the wallpaper was still zooming and its first frames carried the transitional background - the single visible change reported after the desktop looks settled. The retained frame, which already carries the settled portrait sample, is held through that window (WALLPAPER_RETURN_SETTLE_MS), with a timed fallback so a host without a working commit listener cannot strand its capture in the paused state. The capture is now also paused before the wallpaper zoom and the departure state held across an app launch, so the retained frame is the settled portrait one. Host suite: 12/12 groups pass.
The Dock panel sampled the wallpaper through the wrong transform after a landscape app returned to the portrait launcher, and the earlier workarounds only traded that artefact for a compositor-fallback flash. Three independent causes, each confirmed on device: - The host is a windowless SurfaceControlViewHost, so it never receives a relayout. A host created while the display was rotated inherits the vendor's wmRot=1 and keeps it forever; back in portrait every sampled frame is mapped transposed. ViewRootImpl's own checkConfigRot() cannot repair it because it recomputes the same stale merged configuration. - The geometry gate had been removed from the readiness decision while the dock_glass_sync_geometry path it relied on had no call site at all. - The first returning frame still carries the closed app's pass-blur content, and updateTextureState(false) selects the vendor's undraw state, so a paused capture left the panel with nothing to composite. Fixes: - New Android-free DockGlassGeometry policy. The host pins mConfigRot to its NATURAL window rotation (installOrientation + 0), resizes mDispRect and the SurfaceTexture buffer to match, and hands the transform to the renderer with View.setTextureAvailable() without calling checkConfigRot() again - that would undo the correction with the same stale wmRot. backgroundReady is gated on geometryValid plus a bounded captureFresh timestamp check. - The capture pause becomes a drawable freeze (mTextureVis=false, mLastSfState=2, sendSfState(1)): the vendor keeps compositing the last home frame instead of undrawing, so no compositor fallback is needed at all. The freeze runs on the app-launch wallpaper command and on rotation, and thaws through resumeAfterRotation (120 ms) and the home command. - resumeAfterRotation resumes the frozen capture directly instead of through the pose-commit refresh path that a traversal resume could supersede. - DockGlassRecoveryGate.forceRefresh() keeps the rotation probe from being collapsed by the return deduplication window. Also commits the in-flight OS4 dock work the tree was carrying: the AUTO_AIM live-projection unlock style with perspective_fold / capsule_fission and their strings, the native unlock_scale_entry resolver/asm support, and the host test suite updates (13/13 passing, including expectations that had gone stale). Verified on device: returning from a landscape app paints native glass with the correct wallpaper sample.
…site The hooked `scaleValue=` setter is a generic raw-double setter shared with unrelated widgets, so the receiver-chain filter could not separate the Hotseat projection from other callers. The resolver now also records the return address of the `bl` that feeds the setter from `conversionValueFrom3DTo2D`, and the assembly accepts only that call site before falling back to the receiver chain. `dock_unlock_call_return_<bank>` carries the absolute address through the resolver, the hooks and the per-bank trampolines; the resolver, ARM64 and endpoint tests cover its relocation and contract. The pipeline line now reports `aimEntry/aimPublish/aimReject rx/widget/cell/container` so a filter change can be attributed from a device log alone.
The unlock reveal was scaling the Dock about its own centre, so the fly-in read
as an in-place grow with no perspective, and AUTO_AIM forced opacity to 1 from
the first frame.
The launcher's own logs give the real geometry. `prepareUserPresentAnimation`
prints childX/childY, pivotX/pivotY, zPosition and camDis for all 29 items;
`UserPresentAnimation[prepare]` prints the resulting pose. From those:
* the start offset of every item equals `(child - pivot) * s` with one s for
both axes, so the animation is a uniform scale about `pivotPoint`;
* `zPosition = -1.71882 * |child - pivot|`, and the printed footprint is
`camDis / (camDis + zPosition)` to ten significant digits;
* `pivotPoint` is `frameWidth / 2, 0.32572 * frameHeight`.
The Hotseat row therefore starts at ~15% of its size, mirrored to the other
side of the unlock point, and travels down to its place.
AUTO_AIM now replays that: `projectedStartScale(radius)` evaluates the
launcher's own expression for the Dock's radius, the layer is scaled about the
shared pivot instead of its own middle, and opacity follows the same projected
footprint, staying at zero until the Dock is within the last quarter of its
journey. The authored 18 degree tilt, depth offset and 72dp rise are gone, as
is the forced opacity. A fresh native scene-3 sample still wins when present.
Recorded in `DockUnlockRevealTest` against the launcher's logged numbers. The
per-frame time curve remains this class's own; only the trajectory is sampled.
User-requested labels only: the preference keys, the stored values and the `DockUnlockReveal.Style` constants are untouched, so an installed device keeps its selection and no migration is needed. gale swing in from the side -> park out to the right / 向右泊出 orbit_sweep orbital sweep -> enter diagonally / 斜向进入 ripple contract and calm -> calm and composed / 泰然处之 capsule_fission capsule fission -> elastic deformation / 弹性形变 auto_aim auto aim -> gravitational potential / 重力势能 zh-rTW and zh-rHK use the local wording 彈性形變 / 重力位能.
…2% of a core The health worker re-ran the full hook maintenance pass every 250 ms, and every pass rebuilt the whole /proc/self/maps inventory - once at the top of the pass and twice per bank inside bank_healthy(), because the validated read exists to prove that the same image generation is still mapped at the recorded offsets. Measured on-device with `top -H`: 22.2% of one core, permanently, inside the launcher process - gated only on the process name, so it was paid even by users who never enabled the Dock. That is what "removing the launcher from the module's scope drops power use a lot" was. - Steady state now verifies each slot by reading its recorded patch words (read_patch_words): a slot whose live words still equal the recorded patch is a working patch, and a replaced image fails that check and falls straight into the validated repair path. The maps-based proof runs only when a scan is due - force, a periodic backstop, or an unhealthy pass - and every reader goes through acquire_inventory(), because dereferencing that optional without it crashed the health worker with a heap fault in the launcher (SIGSEGV in hc-dock-health). - Health cadence 250 ms -> 1 s while healthy (2 s while repairing): the pass is nearly free now, so the cadence only bounds how long a kernel-restored patch can stay missing. - maybe_install_madvise_guard()'s own maps walk runs every 40th pass instead of every 4th. - Both native threads are named (hc-dock-health, hc-dock-motion). Identifying them by TID cost far more than the one line it takes to label them. - The Dock preference now gates the whole chain: nativeSetDockEnabled() from the launcher-side Java keeps the health worker asleep and the publisher silent while the feature is off, so a disabled Dock costs the launcher nothing. Fail-open on the native side, so a missing or stale push can never silently disable real-time following. Measured after the change: hc-dock-health 22.2% -> ~2% (0.0% in most samples), with subscribed=1 healthy=1 and the entry/publish counters still advancing.
junhaoyyds
added a commit
to junhaoyyds/HyperCeiler
that referenced
this pull request
Sep 14, 2026
…y-in styles rename + native health worker fix
… panel gate it shares The HyperOS 4 desktop is a dex-less Flutter/Rust app (libapp_launcher.so + libapp.so, no Dalvik classes), so every layout target has to be located from the image's own bytes: the runtime resolves the cell-count getters from the ELF program headers and the .gnu_debugdata symbol table, decodes each geometry knob's field path out of the accessor that reads it, and installs inline hooks through the shared NativeHookRuntime. Nothing is addressed by a fixed offset - a launcher OTA moves everything and this has to follow. One commit because the pieces link together: CMakeLists.txt adds the sources that the module's entry points in hyperceiler_home.cpp call, and HomeDockWindow.kt serves both the layout transaction and the dock's panel state. What it adds: - targets/home/home_layout_*: the Rust/Dart target resolver (ELF segments, .eh_frame_hdr function bounds, adrp/add dual identification), the hook bank wiring, the synchronous preference client, the Dart trampolines and the fatal-memory-access guard. - targets/home/tweaks: the third-party (Apache-2.0) planner that locates and patches the grid, icon-size and recents-clear sites from the symbol table or an exactly-once signature. - nativehook/memory_io.h + nativehook/xz: the validated read and guarded patch write, moved out of the dock so both features use one implementation, and the xz decoder the debug data needs. - HomeLayoutNativeEndpointOS4 + SharedPrefsProvider + home_layout.xml: the authenticated snapshot the launcher asks system_server for, the provider rows it reads and the settings page. - tests/home-layout-native: host tests for the resolver, the EH-frame walker, and the endpoint's access checks and value mapping. Power work in the same batch: - The layout health pass rebuilt the whole /proc/self/maps inventory twice per armed slot every 500 ms - 24 full parses per pass, 48 per second, ungated. Steady state now reads the armed slots' live words (ordered_slots_healthy in hook_bank.h, one safe_read per slot) and hands off to the validated repair path only when a patch is actually missing; the generation proof moved to a 30 s backstop that shares one inventory across every slot. Measured on device: hc-home-layout 0.0% of a core, 0.14 s of CPU for the whole process lifetime. - The worker reuses the dock's panel state: while the panel dozes the desktop cannot draw, so the pass is skipped (10 s cadence, shorter than the dock's 30 s because this one owns on-screen geometry, and a patch lost overnight has to be back before the next layout). - LoadConfigBlob's mtime is finally used: the monitor pass stats the candidates and skips the read when the newest one has not moved, instead of opening and parsing up to eight paths on every pass. - Both of this feature's threads are named (hc-home-layout, hc-home-tweaks) - the audit that produced these fixes could not attribute its loops without them. - The worker's four start gates are no longer silent, and the state they read is adopted per process (pid-stamped): HYOS forks the desktop out of a process that shares this code, so a desktop could inherit the parent's "already running" verdict and never create a worker at all. Observed on device as a launcher carrying three of its four threads. - refreshFromProvider() stops at the first refused key and backs off to 30 s: MIUI's app freezer answers every query to the frozen module app with "Outgoing transactions from this process must be FLAG_ONEWAY", so the old loop paid eighteen more cross-process calls and eighteen more stack traces per cycle, forever, and never succeeded. - The dock's failure path backs off 2 s -> 60 s instead of re-running a full image copy and pattern scan every 2 s when a hook cannot be installed (capped, never exhausted, so a build that starts matching self-heals), its health worker is gated on the panel, and the Dock preference gates the whole chain. Verified: ./gradlew :app:assembleRelease --offline (C++ -Wall -Wextra -Werror, R8) and the host tests in tests/home-layout-native. On device all four native threads are present and named, the layout worker logs on its 10 s cadence, and hc-home-layout measures 0.0% of a core.
- resolver: borrow the header ABI for allocation stubs whose class guard was devirtualized away (7654+); two-pass factories scan, class checks still gate - launcher_apk: fall back to the tweaks module's dl_iterate_phdr-proven image path when the /data directory pattern does not match (/product builds) - worker: a failed geometry locate no longer blocks hook knobs; guard the now-optional Located reads - symtab: candidate accessors for folder padding probing and calibration - cmake: warning switches for the layout targets
- worker gate: the tweaks enable flags were not part of the early-out, so a desktop with only icon scale enabled pushed no config at all and every code-patch feature silently stayed off (folder cols / icon scale / recents) - icon scale: the settings default level is 70 (the system default size), not 100; xml default, endpoint fallback and the native kIconScaleCodeDefault (0x66 = 0.6875, the nearest representable code) all move together, and the misleading '100 keeps it unchanged' wording is corrected - features 16/19: an unset count now answers 'leave the launcher alone' instead of clamping to a compiled default
The native-bind sweep had no doze gate: with the panel asleep it kept a 1-2 s cadence in system_server (identity re-bind, preference re-read, rotation gate) for layers that cannot possibly matter - measured as a battery-report entry. A non-interactive panel now stretches the sweep to 30 s (the same policy the launcher's native health worker runs); the first interactive traversal restores the fast cadence.
- HotseatMargin: wired to GridController.hotSeatsMarginBottom — on 7695 the accessor feeds WidgetPositionUtil._getCellRectInHotSeatPad, a +20 delta moved the hotseat row (confirmed on device); the settings slider now drives it live through the existing knobs[0] channel - Dock height: retired. hotSeatsHeight is read once by GlobalHotseatWindowManager.updateInsets at window build time, so a live delta does nothing (probe: d=228 published, 0 hits) and a pending delta lands all at once at the next rebuild, jumping the dock. Settings entry removed, the endpoint forces its slot off, the native symbol stays null - folder square fix restored: disabling it did not stop the close-animation misalignment, so it is unrelated; the non-official column count itself conflicts with the desktop's own close animation (known limitation)
When LSPosed fails to dispatch the module into system_server (early-boot APK parse failure, no retry), the Binder endpoint is gone for the whole system_server lifetime and every consumer-side feature went dark with it. query_config now persists each successfully received config to the launcher's private files directory and falls back to that last-known snapshot when the endpoint is unavailable, so tweaks and knobs keep running with the user's values instead. Dock-glass/WMS-side hooks cannot be saved this way (they live in the dead process). Debug-override configs are never cached.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
总览
适配 HyperOS 4 桌面相关 Hook,新增 OS4 对应的 HomePhone / HomePad 加载入口,并限制原 OS3 Hook 的版本范围。
适配 HyperOS 4 Dock 柔光玻璃背景。
适配 HyperOS 4 最近任务动画,使 Dock 背景能够跟随原生 Recents 动画状态变化。