Skip to content

系统桌面:适配 OS4 底栏柔光玻璃,并修复 OS4 下底栏修改失效问题,适配部分布局功能 - #1686

Open
LDS-XiaoYe wants to merge 50 commits into
ReChronoRain:mainfrom
LDS-XiaoYe:os4-branch
Open

LDS-XiaoYe wants to merge 50 commits into
ReChronoRain:mainfrom
LDS-XiaoYe:os4-branch

Conversation

@LDS-XiaoYe

Copy link
Copy Markdown

总览

适配 HyperOS 4 桌面相关 Hook,新增 OS4 对应的 HomePhone / HomePad 加载入口,并限制原 OS3 Hook 的版本范围。
适配 HyperOS 4 Dock 柔光玻璃背景。
适配 HyperOS 4 最近任务动画,使 Dock 背景能够跟随原生 Recents 动画状态变化。

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.
@LDS-XiaoYe LDS-XiaoYe changed the title 系统桌面-底栏适配OS4柔光玻璃 系统桌面:适配 OS4 底栏柔光玻璃,并修复 OS4 下底栏修改失效问题 Sep 4, 2026
@codacy-production

codacy-production Bot commented Sep 4, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 50 critical · 25 high · 8 medium · 17 minor

Alerts:
⚠ 100 issues (≤ 0 issues of at least minor severity)

Results:
100 new 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

View in Codacy

🟢 Metrics 7541 complexity · 82 duplication

Metric Results
Complexity 7541
Duplication 82

View in Codacy

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.

- 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
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.
@LDS-XiaoYe
LDS-XiaoYe requested a review from ghhccghk September 12, 2026 12:43
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.
@LDS-XiaoYe LDS-XiaoYe changed the title 系统桌面:适配 OS4 底栏柔光玻璃,并修复 OS4 下底栏修改失效问题 系统桌面:适配 OS4 底栏柔光玻璃,并修复 OS4 下底栏修改失效问题,适配部分布局功能 Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants