Problem
On Xiaomi HyperOS (tested on Xiaomi 13 Ultra, HyperOS 2.x), when the user activates one-handed mode (单手模式), the SurfaceFlinger compositor applies a display-area transform that shifts the entire screen content downward. The One Patch blocker rectangle, regardless of which attach mechanism is used, drifts with this transform — defeating the original intent of "pinning the blocker at a fixed physical screen region".
Approaches Tried (All Failed Real-Device Validation)
| # |
Approach |
Status |
| 1 |
TYPE_ACCESSIBILITY_OVERLAY (v1, trusted overlay) |
❌ Drifts. Trusted ≠ transform-immune. |
| 2 |
AccessibilityService.attachAccessibilityOverlayToDisplay() (Android 14+, display-attached SurfaceControl) |
❌ Drifts. HyperOS forks SurfaceFlinger and applies the transform to display-attached layers as well. |
| 3 |
Reverse LayoutParams compensation (closed-loop OnPreDrawListener + getLocationOnScreen ↔ reverse update) |
⚠️ Partial. getLocationOnScreen returns the ViewRootImpl-reported position, which on HyperOS does NOT fully reflect the underlying SurfaceFlinger transform. Compensation eliminates "reported drift" but leaves residual visual mismatch. |
Why Public APIs Cannot Solve This
Verified via grok-search multi-angle research + AOSP source review:
| Signal |
HyperOS behavior |
| Broadcast Intent |
None |
onConfigurationChanged / Configuration.screenHeightDp |
Not triggered |
WindowInsets / onApplyWindowInsetsListener |
Insets unchanged |
DisplayManager.DisplayListener.onDisplayChanged |
Inconsistent across ROMs |
Settings.System ContentObserver |
Key names ROM-private; not triggered via gesture |
View.getMatrix() |
Reflects only view-local matrix |
AccessibilityService.onMotionEvent() selective drop |
API returns void; cannot consume |
TouchInteractionController |
Requires touch-exploration; conflicts with TalkBack |
Reflection on ViewRootImpl.mTranslator / mInvCompatScale |
@hide; API 28+ reflection denylist |
The only working path is what Xiaomi's Game Turbo uses — inputflinger injection in system_server — which requires platform-signature privileges unavailable to third-party apps.
Position Coordinate Three-Layer Model
Android exposes only one of three layers to apps:
- (a) Physical pixel position — hardware level, NO public API
- (b) SurfaceFlinger composited position —
dumpsys SurfaceFlinger / Winscope only
- (c) ViewRootImpl-reported position —
getLocationOnScreen(), the ONLY signal apps can read; ROM-dependent accuracy
Current Best-Effort Behavior (shipped)
User-toggleable in settings (屏蔽区物理坐标锁定):
- ON (default): Reverse LayoutParams compensation. Eliminates reported drift; residual compositor drift on HyperOS unavoidable.
- OFF: Legacy step-aside behavior. Blocker auto-pauses when displaced, resumes on full-screen.
Acceptance
Public-API platform limitation, not a bug. Keeping open as research thread:
- If a future Android API exposes compositor transform queries → revisit
- If HyperOS aligns SurfaceFlinger transform with getLocationOnScreen → revisit
- If platform-signed alternative becomes feasible → revisit
Workaround for HyperOS Users
Disable 屏蔽区物理坐标锁定 switch — accept v1 step-aside behavior during one-handed mode.
Related Archived Specs
- openspec/changes/archive/2026-04-28-fix-fab-bugs/
- openspec/changes/archive/2026-04-28-physical-display-overlay/
- openspec/changes/archive/2026-04-29-transform-compensation-overlay/
Problem
On Xiaomi HyperOS (tested on Xiaomi 13 Ultra, HyperOS 2.x), when the user activates one-handed mode (单手模式), the SurfaceFlinger compositor applies a display-area transform that shifts the entire screen content downward. The One Patch blocker rectangle, regardless of which attach mechanism is used, drifts with this transform — defeating the original intent of "pinning the blocker at a fixed physical screen region".
Approaches Tried (All Failed Real-Device Validation)
TYPE_ACCESSIBILITY_OVERLAY(v1, trusted overlay)AccessibilityService.attachAccessibilityOverlayToDisplay()(Android 14+, display-attached SurfaceControl)getLocationOnScreen↔ reverse update)getLocationOnScreenreturns the ViewRootImpl-reported position, which on HyperOS does NOT fully reflect the underlying SurfaceFlinger transform. Compensation eliminates "reported drift" but leaves residual visual mismatch.Why Public APIs Cannot Solve This
Verified via grok-search multi-angle research + AOSP source review:
onConfigurationChanged/Configuration.screenHeightDpWindowInsets/onApplyWindowInsetsListenerDisplayManager.DisplayListener.onDisplayChangedSettings.SystemContentObserverView.getMatrix()AccessibilityService.onMotionEvent()selective dropTouchInteractionControllerViewRootImpl.mTranslator/mInvCompatScale@hide; API 28+ reflection denylistThe only working path is what Xiaomi's Game Turbo uses —
inputflingerinjection in system_server — which requires platform-signature privileges unavailable to third-party apps.Position Coordinate Three-Layer Model
Android exposes only one of three layers to apps:
dumpsys SurfaceFlinger/ Winscope onlygetLocationOnScreen(), the ONLY signal apps can read; ROM-dependent accuracyCurrent Best-Effort Behavior (shipped)
User-toggleable in settings (
屏蔽区物理坐标锁定):Acceptance
Public-API platform limitation, not a bug. Keeping open as research thread:
Workaround for HyperOS Users
Disable
屏蔽区物理坐标锁定switch — accept v1 step-aside behavior during one-handed mode.Related Archived Specs