Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d96c9fb authored by Mashopy's avatar Mashopy Committed by Rohit Sekhar
Browse files

SystemUI: Reverse MediaTek udfps dimlayer changes



The current impl for udfps framework dimming mostly targets OPlus Qualcomm devices, making it
unsuitable for MediaTek devices lacking LHBM supports.

Due to how GHBM is handled by MediaTek on BSP (HWC + RenderEngine + SysUI), it causes multiple issues
on them, notably the infamous flashbang behavior with GHBM as MediaTek HWC expect multiple DimLayers that dynamically parse
alpha based on the current brightness value and then order renderengine to dither it to not trigger a flashbang.

This behavior has been mostly reversed from PRIZE MtkSystemUI apk from LAVA and tested on Nothing Phone (2a).

TODO: Test on non-PRIZE devices and possibly let other devices change the alpha smoother point.
Change-Id: I907d9075c79ab4af5b82d3781291d97c1eab17b1
Signed-off-by: default avatarHeroBuxx <me@herobuxx.me>
parent c4c07dae
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -57,6 +57,12 @@
          <item>4095,0</item>
    </string-array>

    <!-- Whether to enable MediaTek-specific optical dimming for UDFPS -->
    <bool name="config_udfpsMtkGhbmDimming">false</bool>

    <!-- Name of the UDFPS HBM DimLayer for the HWComposer -->
    <string name="config_udfpsHbmDimLayer" translatable="false">OnScreenFingerprintDimLayer</string>

    <!-- Doze: does the double tap sensor need a proximity check? -->
    <bool name="doze_double_tap_proximity_check">false</bool>

+95 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2026 The LineageOS project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.biometrics;

import android.content.Context;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;

/**
 * Controller for managing the UDFPS HBM (High Brightness Mode) Scrim on MediaTek devices lacking LHBM support.
 *
 * Logic reversed from Smali: Lcom/android/systemui/biometrics/PriUdfpsScrimController; from LAVA
 */
public class MtkUdfpsScrimController {
    private static MtkUdfpsScrimController mInstance;

    // Accessed directly by UdfpsController
    public View mScrimView;
    public WindowManager mWindowManager;

    private final Context mContext;

    public MtkUdfpsScrimController(Context context) {
        mContext = context;
    }

    public static MtkUdfpsScrimController getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new MtkUdfpsScrimController(context);
        }
        return mInstance;
    }

    /**
     * Calculates the alpha (transparency) for the HBM overlay based on screen brightness.
     * The formula ensures the sensor gets a consistent amount of light.
     *
     * @param brightness Current system brightness (0-255)
     * @return Alpha value (0.0f to 1.0f)
     */
    public float calculateAlpha(int brightness) {
        float alpha = 1.0f - (brightness / 255.0f);

        if (brightness < 25) {
            // Make alpha 5% more transparent when brightness is under 25.
            alpha = alpha * 0.95f;
        }

        Log.d("MtkUdfpsScrimController", "Requested Brightness value: " + brightness);
        Log.d("MtkUdfpsScrimController", "Alpha Value: " + alpha);

        // Ensure we stay in bounds
        return Math.max(0.0f, Math.min(1.0f, alpha));
    }

    public int getSystemBrightness() {
        if (mContext == null) {
            return 127;
        }

        // Try Float first
        float brightFloat = Settings.System.getFloat(
            mContext.getContentResolver(),
            "screen_brightness_float",
            -1.0f
        );

        if (brightFloat >= 0.0f) {
            return (int) (brightFloat * 255.0f);
        }

        // Fallback
        return Settings.System.getInt(
            mContext.getContentResolver(),
            Settings.System.SCREEN_BRIGHTNESS,
            127
        );
    }
}
+47 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import com.android.keyguard.UserActivityNotifier;
import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.biometrics.MtkUdfpsScrimController;
import com.android.systemui.biometrics.dagger.BiometricsBackground;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
@@ -227,6 +228,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
    private boolean mAttemptedToDismissKeyguard;
    private final Set<Callback> mCallbacks = new HashSet<>();

    private boolean mUseMtkGhbmDimming;

    @VisibleForTesting
    public static final VibrationAttributes UDFPS_VIBRATION_ATTRIBUTES =
            new VibrationAttributes.Builder()
@@ -775,6 +778,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {

        udfpsHapticsSimulator.setUdfpsController(this);
        udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController);

        mUseMtkGhbmDimming = mContext.getResources().getBoolean(
            com.android.systemui.res.R.bool.config_udfpsMtkGhbmDimming);
    }

    /**
@@ -847,6 +853,15 @@ public class UdfpsController implements DozeReceiver, Dumpable {
            if (oldView != null) {
                onFingerUp(mOverlay.getRequestId(), oldView);
            }

            // Ensure HBM views are removed from WindowManager
            if (mUseMtkGhbmDimming) {
                View hbmView = mOverlay.getHbmView();
                if (hbmView != null) {
                    hbmView.setVisibility(View.GONE);
                }
            }

            final boolean removed = mOverlay.hide();
            mKeyguardViewManager.hideAlternateBouncer(true);
            Log.v(TAG, "hideUdfpsOverlay | removing window: " + removed);
@@ -1064,6 +1079,30 @@ public class UdfpsController implements DozeReceiver, Dumpable {
        mOnFingerDown = true;
        mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
                minor, major, orientation, time, gestureStart, isAod);

        if (mUseMtkGhbmDimming && mOverlay != null) {
            View hbmView = mOverlay.getHbmView(); // Ensure UdfpsControllerOverlay exposes this
            WindowManager.LayoutParams hbmParams = mOverlay.getHbmLayoutParamsFull();

            if (hbmView != null && hbmParams != null) {
                MtkUdfpsScrimController mtkController = MtkUdfpsScrimController.getInstance(mContext);

                int brightness = mtkController.getSystemBrightness();
                float alpha = mtkController.calculateAlpha(brightness);

                Log.d(TAG, "UDFPS Scrim: Brightness=" + brightness + " CalculatedAlpha=" + alpha);

                if (hbmView.getVisibility() != View.VISIBLE) {
                    hbmView.setVisibility(View.VISIBLE);
                }

                if (Math.abs(hbmParams.alpha - alpha) > 0.001f) {
                    hbmParams.alpha = alpha;
                    mWindowManager.updateViewLayout(hbmView, hbmParams);
                }
            }
        }

        Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);

        final View view = mOverlay.getTouchOverlay();
@@ -1122,6 +1161,14 @@ public class UdfpsController implements DozeReceiver, Dumpable {
            }
        }
        mOnFingerDown = false;

        if (mUseMtkGhbmDimming && mOverlay != null) {
            View hbmView = mOverlay.getHbmView();
            if (hbmView != null) {
                hbmView.setVisibility(View.GONE);
            }
        }

        unconfigureDisplay(view);
        cancelAodSendFingerUpAction();
    }
+181 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.biometrics
import android.annotation.SuppressLint
import android.annotation.UiThread
import android.content.Context
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.Rect
import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
@@ -124,7 +125,53 @@ constructor(
        null
    }

    private val coreLayoutParams =
    private val useMtkGhbmDimming = context.resources.getBoolean(
        com.android.systemui.res.R.bool.config_udfpsMtkGhbmDimming
    )

    private val hbmDimLayerName = context.resources.getString(
        com.android.systemui.res.R.string.config_udfpsHbmDimLayer
    )

    val hbmView: View? = if (useMtkGhbmDimming) {
        View(context).apply {
            setBackgroundColor(Color.BLACK)
            visibility = View.INVISIBLE
        }
    } else null

    private val dimView: View? = if (useMtkGhbmDimming) {
        View(context).apply {
            setBackgroundColor(Color.TRANSPARENT)
        }
    } else null

    private var isAddDimView: Boolean = false

    private val coreLayoutParams: WindowManager.LayoutParams = if (useMtkGhbmDimming) {
            WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                0 /* flags set in computeLayoutParams() */,
                PixelFormat.TRANSLUCENT
            ).apply {
                title = TAG
                fitInsetsTypes = 0
                gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
                layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

                flags = (WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED or
                    WindowManager.LayoutParams.FLAG_SPLIT_TOUCH or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)

                privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
                // Avoid announcing window title.
                accessibilityTitle = " "

                inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
            }
    } else {
            WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                0 /* flags set in computeLayoutParams() */,
@@ -144,6 +191,92 @@ constructor(
                accessibilityTitle = " "
                inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
            }
    }

    // HBM Params (The high brightness layer)
    private val hbmLayoutParams: WindowManager.LayoutParams? = if (useMtkGhbmDimming) {
            WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                0 /* flags set in computeLayoutParams() */,
                PixelFormat.TRANSLUCENT
            ).apply {
                title = hbmDimLayerName
                fitInsetsTypes = 0
                alpha = 0.1f
                gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
                layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

                flags = (WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED or
                    WindowManager.LayoutParams.FLAG_SPLIT_TOUCH or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)

                privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
                // Avoid announcing window title.
                accessibilityTitle = " "

                inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
            }
    } else {
        null
    }

    // HBM Full Params
    val hbmLayoutParamsFull: WindowManager.LayoutParams? = if (useMtkGhbmDimming) {
            WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                0 /* flags set in computeLayoutParams() */,
                PixelFormat.TRANSLUCENT
            ).apply {
                title = hbmDimLayerName
                fitInsetsTypes = 0
                alpha = 0.1f
                gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
                layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

                flags = (WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED or
                    WindowManager.LayoutParams.FLAG_SPLIT_TOUCH or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)

                privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
                // Avoid announcing window title.
                accessibilityTitle = " "

                inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
            }
    } else {
        null
    }

    // Dim Params
    private val dimLayoutParams: WindowManager.LayoutParams? = if (useMtkGhbmDimming) {
            WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                0 /* flags set in computeLayoutParams() */,
                PixelFormat.TRANSLUCENT
            ).apply {
                title = "UdfpsDim"
                fitInsetsTypes = 0
                gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
                layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

                flags = (WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED or
                    WindowManager.LayoutParams.FLAG_SPLIT_TOUCH or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)

                privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
                accessibilityTitle = " "

                inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
            }
    } else {
        null
    }

    /** If the overlay is currently showing. */
    val isShowing: Boolean
@@ -230,8 +363,36 @@ constructor(
        addViewRunnable =
            kotlinx.coroutines.Runnable {
                Trace.setCounter("UdfpsAddView", 1)

                if (useMtkGhbmDimming) {
                    hbmLayoutParams?.updateDimensions(animation)
                    hbmLayoutParamsFull?.updateDimensions(animation)
                    dimLayoutParams?.updateDimensions(animation)

                    try {
                        windowManager.addView(hbmView, hbmLayoutParams)
                        windowManager.addView(dimView, dimLayoutParams)
                        isAddDimView = true
                    } catch (e: Exception) {
                        Log.e(TAG, "Failed to add vendor HBM/Dim views", e)
                    }

                    windowManager.addView(view, coreLayoutParams.updateDimensions(animation))

                    if (requestReason == REASON_ENROLL_FIND_SENSOR || requestReason == REASON_ENROLL_ENROLLING) {
                        val child = view.findViewById<View>(R.id.udfps_enroll_accessibility_view)
                        child?.let {
                            val lp = it.layoutParams
                            lp.width = sensorBounds.width()
                            lp.height = sensorBounds.height()
                            it.layoutParams = lp
                            it.requestLayout()
                        }
                    }
                } else {
                    windowManager.addView(view, coreLayoutParams.updateDimensions(animation))
                }
            }
        if (powerInteractor.detailedWakefulness.value.isAwake()) {
            // Device is awake, so we add the view immediately.
            addViewIfPending()
@@ -261,6 +422,12 @@ constructor(
                // no need to update any layouts. Instead the correct params will be used when the
                // view is eventually added.
                windowManager.updateViewLayout(it, coreLayoutParams.updateDimensions(null))

                if (useMtkGhbmDimming && isAddDimView) {
                    hbmLayoutParamsFull?.updateDimensions(null)
                    windowManager.updateViewLayout(hbmView, hbmLayoutParams)
                    windowManager.updateViewLayout(dimView, dimLayoutParams)
                }
            }
        }
    }
@@ -280,6 +447,17 @@ constructor(
            if (this.parent != null) {
                windowManager.removeView(this)
            }

            if (useMtkGhbmDimming && isAddDimView) {
                try {
                    windowManager.removeView(hbmView)
                    windowManager.removeView(dimView)
                } catch (e: Exception) {
                    Log.w(TAG, "Failed to remove HBM/Dim views", e)
                }
                isAddDimView = false
            }

            Trace.setCounter("UdfpsAddView", 0)
            setOnTouchListener(null)
            setOnHoverListener(null)