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

Commit d602089b authored by Ilya Matyukhin's avatar Ilya Matyukhin
Browse files

Implement a bare-bones UDFPS overlay

Bug: 158135499
Test: Enable the overlay by returning "true" from "isUdfps" from the HAL
and call "showUdfpsOverlay" from "start" in UdfpsController.

Change-Id: I3fb8291e5a918f1d7b48e258aae9ebe756ab2223
parent 1ba66726
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.systemui.udfps.UdfpsView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/udfps_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    systemui:sensorRadius="140px"
    systemui:sensorMarginBottom="630px"
    systemui:sensorTouchAreaCoefficient="0.5"/>
+7 −0
Original line number Diff line number Diff line
@@ -161,5 +161,12 @@
        <attr name="rippleMinSize" format="dimension" />
        <attr name="rippleMaxSize" format="dimension" />
    </declare-styleable>

    <declare-styleable name="UdfpsView">
        <attr name="sensorRadius" format="dimension"/>
        <attr name="sensorMarginBottom" format="dimension"/>
        <attr name="sensorPressureCoefficient" format="float"/>
        <attr name="sensorTouchAreaCoefficient" format="float"/>
    </declare-styleable>
</resources>
+7 −0
Original line number Diff line number Diff line
@@ -2840,4 +2840,11 @@
    <string name="controls_menu_add">Add controls</string>
    <!-- Controls menu, edit [CHAR_LIMIT=30] -->
    <string name="controls_menu_edit">Edit controls</string>

    <!-- Device-specific path to the node for enabling/disabling the high-brightness mode -->
    <string name="udfps_hbm_sysfs_path" translatable="false"></string>
    <!-- Device-specific payload for enabling the high-brightness mode -->
    <string name="udfps_hbm_enable_command" translatable="false"></string>
    <!-- Device-specific payload for disabling the high-brightness mode -->
    <string name="udfps_hbm_disable_command" translatable="false"></string>
</resources>
+29 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
@@ -37,10 +38,12 @@ import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.view.WindowManager;

@@ -76,6 +79,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,

    private Handler mHandler = new Handler(Looper.getMainLooper());
    private WindowManager mWindowManager;
    private UdfpsController mUdfpsController;
    @VisibleForTesting
    IActivityTaskManager mActivityTaskManager;
    @VisibleForTesting
@@ -242,6 +246,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
        IActivityTaskManager getActivityTaskManager() {
            return ActivityTaskManager.getService();
        }

        IFingerprintService getFingerprintService() {
            return IFingerprintService.Stub.asInterface(
                    ServiceManager.getService(Context.FINGERPRINT_SERVICE));
        }
    }

    @Inject
@@ -267,6 +276,26 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        mActivityTaskManager = mInjector.getActivityTaskManager();

        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
            IFingerprintService fingerprintService = mInjector.getFingerprintService();
            if (fingerprintService == null) {
                Log.e(TAG, "FEATURE_FINGERPRINT is available, but FingerprintService is null");
            } else {
                boolean isUdfps = false;
                try {
                    // TODO(b/160024833): Enumerate through all of the sensors and check whether
                    //  at least one of them is UDFPS.
                    isUdfps = fingerprintService.isUdfps(0 /* sensorId */);
                } catch (RemoteException e) {
                    Log.w(TAG, "Unable to check whether the sensor is a UDFPS", e);
                }
                if (isUdfps) {
                    mUdfpsController = new UdfpsController(mContext, fingerprintService,
                            mWindowManager);
                }
            }
        }

        try {
            mTaskStackListener = new BiometricTaskStackListener();
            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+191 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source 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.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.RemoteException;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.LinearLayout;

import com.android.systemui.R;

import java.io.FileWriter;
import java.io.IOException;

/**
 * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
 * and coordinates triggering of the high-brightness mode (HBM).
 */
class UdfpsController {
    private static final String TAG = "UdfpsController";

    private final Context mContext;
    private final IFingerprintService mFingerprintService;
    private final WindowManager mWindowManager;

    private UdfpsView mView;
    private WindowManager.LayoutParams mLayoutParams;
    private String mHbmPath;
    private String mHbmEnableCommand;
    private String mHbmDisableCommand;
    private boolean mIsOverlayShowing;

    public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
        @Override
        public void showUdfpsOverlay() {
            UdfpsController.this.showUdfpsOverlay();
        }

        @Override
        public void hideUdfpsOverlay() {
            UdfpsController.this.hideUdfpsOverlay();
        }
    }

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

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mView.isFingerDown()) {
                    onFingerUp();
                }
                return true;

            default:
                return false;
        }
    };

    UdfpsController(Context context, IFingerprintService fingerprintService,
            WindowManager windowManager) {
        mContext = context;
        mFingerprintService = fingerprintService;
        mWindowManager = windowManager;
        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);
        layout.setLayoutParams(mLayoutParams);
        mView = (UdfpsView) LayoutInflater.from(mContext).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);

        try {
            mFingerprintService.setUdfpsOverlayController(new UdfpsOverlayController());
        } catch (RemoteException e) {
            Log.e(TAG, "start | failed to set UDFPS controller", e);
        }
        mIsOverlayShowing = false;
    }

    private void showUdfpsOverlay() {
        Log.v(TAG, "showUdfpsOverlay | adding window");
        if (!mIsOverlayShowing) {
            try {
                mWindowManager.addView(mView, mLayoutParams);
                mIsOverlayShowing = true;
            } catch (RuntimeException e) {
                Log.e(TAG, "showUdfpsOverlay | failed to add window", e);
            }
        }
    }

    private void hideUdfpsOverlay() {
        Log.v(TAG, "hideUdfpsOverlay | removing window");
        if (mIsOverlayShowing) {
            mWindowManager.removeView(mView);
            mIsOverlayShowing = false;
        }
    }

    private void onFingerDown(int x, int y, float minor, float major) {
        try {
            FileWriter fw = new FileWriter(mHbmPath);
            fw.write(mHbmEnableCommand);
            fw.close();
        } catch (IOException e) {
            Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
        }
        mView.onFingerDown();
        try {
            mFingerprintService.onFingerDown(x, y, minor, major);
        } catch (RemoteException e) {
            Log.e(TAG, "onFingerDown | failed to propagate onFingerDown", e);
        }
    }

    private void onFingerUp() {
        try {
            mFingerprintService.onFingerUp();
        } catch (RemoteException e) {
            Log.e(TAG, "onFingeUp | failed to propagate onFingerUp", e);
        }
        mView.onFingerUp();
        try {
            FileWriter fw = new FileWriter(mHbmPath);
            fw.write(mHbmDisableCommand);
            fw.close();
        } catch (IOException e) {
            Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage());
        }
    }
}
Loading