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

Commit 6fbee883 authored by Ilya Matyukhin's avatar Ilya Matyukhin Committed by Android (Google) Code Review
Browse files

Merge changes I4a87c587,If43c21bd

* changes:
  Implement brightness-aware scrim
  Clean up UdfpsController intitialization logic
parents 17749245 4b416fe4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -280,7 +280,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
                    fpm.getSensorProperties();
            for (FingerprintSensorProperties props : fingerprintSensorProperties) {
                if (props.sensorType == FingerprintSensorProperties.TYPE_UDFPS) {
                    mUdfpsController = new UdfpsController(mContext, mWindowManager);
                    mUdfpsController = new UdfpsController(mContext);
                    break;
                }
            }
+149 −43
Original line number Diff line number Diff line
@@ -17,21 +17,27 @@
package com.android.systemui.biometrics;

import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
import android.util.Spline;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.LinearLayout;

import com.android.internal.BrightnessSynchronizer;
import com.android.systemui.R;

import java.io.FileWriter;
@@ -43,17 +49,31 @@ import java.io.IOException;
 */
class UdfpsController {
    private static final String TAG = "UdfpsController";
    // Gamma approximation for the sRGB color space.
    private static final float DISPLAY_GAMMA = 2.2f;

    private final Context mContext;
    private final FingerprintManager mFingerprintManager;
    private final WindowManager mWindowManager;
    private final ContentResolver mContentResolver;
    private final Handler mHandler;

    private UdfpsView mView;
    private WindowManager.LayoutParams mLayoutParams;
    private String mHbmPath;
    private String mHbmEnableCommand;
    private String mHbmDisableCommand;
    private final UdfpsView mView;
    private final WindowManager.LayoutParams mLayoutParams;
    // Debugfs path to control the high-brightness mode.
    private final String mHbmPath;
    private final String mHbmEnableCommand;
    private final String mHbmDisableCommand;
    // Brightness in nits in the high-brightness mode.
    private final float mHbmNits;
    // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to a
    // brightness in nits.
    private final Spline mBacklightToNitsSpline;
    // A spline mapping from a value in nits to a backlight value of a hypothetical panel whose
    // maximum backlight value corresponds to our panel's high-brightness mode.
    // The output is normalized to the range [0, 1.0].
    private Spline mNitsToHbmBacklightSpline;
    // Default non-HBM backlight value normalized to the range [0, 1.0]. Used as a fallback when the
    // actual brightness value cannot be retrieved.
    private final float mDefaultBrightness;
    private boolean mIsOverlayShowing;

    public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@@ -70,22 +90,23 @@ class UdfpsController {

    @SuppressLint("ClickableViewAccessibility")
    private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> {
        UdfpsView view = (UdfpsView) v;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                boolean isValidTouch = mView.isValidTouch(event.getX(), event.getY(),
                boolean isValidTouch = view.isValidTouch(event.getX(), event.getY(),
                        event.getPressure());
                if (!mView.isFingerDown() && isValidTouch) {
                if (!view.isFingerDown() && isValidTouch) {
                    onFingerDown((int) event.getX(), (int) event.getY(), event.getTouchMinor(),
                            event.getTouchMajor());
                } else if (mView.isFingerDown() && !isValidTouch) {
                } else if (view.isFingerDown() && !isValidTouch) {
                    onFingerUp();
                }
                return true;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mView.isFingerDown()) {
                if (view.isFingerDown()) {
                    onFingerUp();
                }
                return true;
@@ -95,43 +116,51 @@ class UdfpsController {
        }
    };

    UdfpsController(Context context, WindowManager windowManager) {
        mContext = context;
    UdfpsController(Context context) {
        mFingerprintManager = context.getSystemService(FingerprintManager.class);
        mWindowManager = windowManager;
        mWindowManager = context.getSystemService(WindowManager.class);
        mContentResolver = context.getContentResolver();
        mHandler = new Handler(Looper.getMainLooper());
        start();
    }

    private void start() {
        Log.v(TAG, "start");

        Point displaySize = new Point();
        mWindowManager.getDefaultDisplay().getRealSize(displaySize);
        // TODO(b/160025856): move to the "dump" method.
        Log.v(TAG, "UdfpsController | display size: " + displaySize.x + "x"
                + displaySize.y);

        mLayoutParams = new WindowManager.LayoutParams(
                displaySize.x,
                displaySize.y,
                // TODO(b/152419866): Use the UDFPS window type when it becomes available.
                WindowManager.LayoutParams.TYPE_BOOT_PROGRESS,
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                PixelFormat.TRANSLUCENT);
        mLayoutParams.setTitle(TAG);
        mLayoutParams.windowAnimations = 0;

        LinearLayout layout = new LinearLayout(mContext);
        mLayoutParams = createLayoutParams(context);
        LinearLayout layout = new LinearLayout(context);
        layout.setLayoutParams(mLayoutParams);
        mView = (UdfpsView) LayoutInflater.from(mContext).inflate(R.layout.udfps_view, layout,
        mView = (UdfpsView) LayoutInflater.from(context).inflate(R.layout.udfps_view, layout,
                false);
        mView.setOnTouchListener(mOnTouchListener);

        mHbmPath = mContext.getResources().getString(R.string.udfps_hbm_sysfs_path);
        mHbmEnableCommand = mContext.getResources().getString(R.string.udfps_hbm_enable_command);
        mHbmDisableCommand = mContext.getResources().getString(R.string.udfps_hbm_disable_command);
        mHbmPath = context.getResources().getString(R.string.udfps_hbm_sysfs_path);
        mHbmEnableCommand = context.getResources().getString(R.string.udfps_hbm_enable_command);
        mHbmDisableCommand = context.getResources().getString(R.string.udfps_hbm_disable_command);

        // This range only consists of the minimum and maximum values, which only cover
        // non-high-brightness mode.
        float[] nitsRange = toFloatArray(context.getResources().obtainTypedArray(
                com.android.internal.R.array.config_screenBrightnessNits));

        // The last value of this range corresponds to the high-brightness mode.
        float[] nitsAutoBrightnessValues = toFloatArray(context.getResources().obtainTypedArray(
                com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));

        mHbmNits = nitsAutoBrightnessValues[nitsAutoBrightnessValues.length - 1];
        float[] hbmNitsRange = {nitsRange[0], mHbmNits};

        // This range only consists of the minimum and maximum backlight values, which only apply
        // in non-high-brightness mode.
        float[] normalizedBacklightRange = normalizeBacklightRange(
                context.getResources().getIntArray(
                        com.android.internal.R.array.config_screenBrightnessBacklight));

        mBacklightToNitsSpline = Spline.createSpline(normalizedBacklightRange, nitsRange);
        mNitsToHbmBacklightSpline = Spline.createSpline(hbmNitsRange, normalizedBacklightRange);
        mDefaultBrightness = obtainDefaultBrightness(context);

        // TODO(b/160025856): move to the "dump" method.
        Log.v(TAG, String.format("ctor | mNitsRange: [%f, %f]", nitsRange[0], nitsRange[1]));
        Log.v(TAG, String.format("ctor | mHbmNitsRange: [%f, %f]", hbmNitsRange[0],
                hbmNitsRange[1]));
        Log.v(TAG, String.format("ctor | mNormalizedBacklightRange: [%f, %f]",
                normalizedBacklightRange[0], normalizedBacklightRange[1]));

        mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
        mIsOverlayShowing = false;
@@ -162,7 +191,34 @@ class UdfpsController {
        });
    }

    // Returns a value in the range of [0, 255].
    private int computeScrimOpacity() {
        // Backlight setting can be NaN, -1.0f, and [0.0f, 1.0f].
        float backlightSetting = Settings.System.getFloatForUser(mContentResolver,
                Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBrightness,
                UserHandle.USER_CURRENT);

        // Constrain the backlight setting to [0.0f, 1.0f].
        float backlightValue = MathUtils.constrain(backlightSetting,
                PowerManager.BRIGHTNESS_MIN,
                PowerManager.BRIGHTNESS_MAX);

        // Interpolate the backlight value to nits.
        float nits = mBacklightToNitsSpline.interpolate(backlightValue);

        // Interpolate nits to a backlight value for a panel with enabled HBM.
        float interpolatedHbmBacklightValue = mNitsToHbmBacklightSpline.interpolate(nits);

        float gammaCorrectedHbmBacklightValue = (float) Math.pow(interpolatedHbmBacklightValue,
                1.0f / DISPLAY_GAMMA);
        float scrimOpacity = PowerManager.BRIGHTNESS_MAX - gammaCorrectedHbmBacklightValue;

        // Interpolate the opacity value from [0.0f, 1.0f] to [0, 255].
        return BrightnessSynchronizer.brightnessFloatToInt(scrimOpacity);
    }

    private void onFingerDown(int x, int y, float minor, float major) {
        mView.setScrimAlpha(computeScrimOpacity());
        try {
            FileWriter fw = new FileWriter(mHbmPath);
            fw.write(mHbmEnableCommand);
@@ -185,4 +241,54 @@ class UdfpsController {
            Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage());
        }
    }

    private static WindowManager.LayoutParams createLayoutParams(Context context) {
        Point displaySize = new Point();
        context.getDisplay().getRealSize(displaySize);
        // TODO(b/160025856): move to the "dump" method.
        Log.v(TAG, "createLayoutParams | display size: " + displaySize.x + "x"
                + displaySize.y);
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                displaySize.x,
                displaySize.y,
                // TODO(b/152419866): Use the UDFPS window type when it becomes available.
                WindowManager.LayoutParams.TYPE_BOOT_PROGRESS,
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                PixelFormat.TRANSLUCENT);
        lp.setTitle(TAG);
        lp.windowAnimations = 0;
        return lp;
    }

    private static float obtainDefaultBrightness(Context context) {
        PowerManager powerManager = context.getSystemService(PowerManager.class);
        if (powerManager == null) {
            Log.e(TAG, "PowerManager is unavailable. Can't obtain default brightness.");
            return 0f;
        }
        return MathUtils.constrain(powerManager.getBrightnessConstraint(
                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT), PowerManager.BRIGHTNESS_MIN,
                PowerManager.BRIGHTNESS_MAX);
    }

    private static float[] toFloatArray(TypedArray array) {
        final int n = array.length();
        float[] vals = new float[n];
        for (int i = 0; i < n; i++) {
            vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT);
        }
        array.recycle();
        return vals;
    }

    private static float[] normalizeBacklightRange(int[] backlight) {
        final int n = backlight.length;
        float[] normalizedBacklight = new float[n];
        for (int i = 0; i < n; i++) {
            normalizedBacklight[i] = BrightnessSynchronizer.brightnessIntToFloat(backlight[i]);
        }
        return normalizedBacklight;
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ public class UdfpsView extends View {

        mScrimRect = new Rect();
        mScrimPaint = new Paint(0 /* flags */);
        mScrimPaint.setARGB(110 /* a */, 0 /* r */, 0 /* g */, 0 /* b */);
        mScrimPaint.setColor(Color.BLACK);

        mSensorRect = new RectF();
        mSensorPaint = new Paint(0 /* flags */);
@@ -136,6 +136,10 @@ public class UdfpsView extends View {
                && y < (mSensorY + mSensorRadius * mSensorTouchAreaCoefficient);
    }

    void setScrimAlpha(int alpha) {
        mScrimPaint.setAlpha(alpha);
    }

    boolean isFingerDown() {
        return mIsFingerDown;
    }