diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e86d2ce617b7b..03bb168619707 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3390,6 +3390,23 @@
Finishing boot.
+
+ Turn off screen?
+
+
+ While setting up your fingerprint, you
+ pressed the Power button.\n\nThis usually turns off your screen.
+
+
+ Turn off
+
+
+ Cancel
+
%1$s running
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3a22efce70fc1..b8f82b411969e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1814,6 +1814,10 @@
+
+
+
+
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 27f5350661f0b..0a65c2367e891 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -379,6 +379,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private DisplayFoldController mDisplayFoldController;
AppOpsManager mAppOpsManager;
PackageManager mPackageManager;
+ SideFpsEventHandler mSideFpsEventHandler;
private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -928,6 +929,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else if (count == 3) {
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
} else if (interactive && !beganFromNonInteractive) {
+ if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {
+ Slog.i(TAG, "Suppressing power key because the user is interacting with the "
+ + "fingerprint sensor");
+ return;
+ }
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
@@ -1803,6 +1809,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
});
initKeyCombinationRules();
initSingleKeyGestureRules();
+ mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager);
}
private void initKeyCombinationRules() {
@@ -4668,6 +4675,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mKeyguardDelegate.onBootCompleted();
}
}
+ mSideFpsEventHandler.onFingerprintSensorReady();
startedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
finishedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
diff --git a/services/core/java/com/android/server/policy/SideFpsEventHandler.java b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
new file mode 100644
index 0000000000000..7c0005cf9e803
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021 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.server.policy;
+
+import static android.hardware.fingerprint.FingerprintStateListener.STATE_ENROLLING;
+import static android.hardware.fingerprint.FingerprintStateListener.STATE_IDLE;
+
+import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintStateListener;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Defines behavior for handling interactions between power button events and
+ * fingerprint-related operations, for devices where the fingerprint sensor (side fps)
+ * lives on the power button.
+ */
+public class SideFpsEventHandler {
+ @NonNull private final Context mContext;
+ @NonNull private final Handler mHandler;
+ @NonNull private final PowerManager mPowerManager;
+ @NonNull private final AtomicBoolean mIsSideFps;
+ @NonNull private final AtomicBoolean mSideFpsEventHandlerReady;
+
+ private @FingerprintStateListener.State int mFingerprintState;
+
+ SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager) {
+ mContext = context;
+ mHandler = handler;
+ mPowerManager = powerManager;
+ mFingerprintState = STATE_IDLE;
+ mIsSideFps = new AtomicBoolean(false);
+ mSideFpsEventHandlerReady = new AtomicBoolean(false);
+ }
+
+ /**
+ * Called from {@link PhoneWindowManager} after power button is pressed. Checks fingerprint
+ * sensor state and if mFingerprintState = STATE_ENROLLING, displays a dialog confirming intent
+ * to turn screen off. If confirmed, the device goes to sleep, and if canceled, the dialog is
+ * dismissed.
+ * @param eventTime powerPress event time
+ * @return true if powerPress was consumed, false otherwise
+ */
+ public boolean onSinglePressDetected(long eventTime) {
+ if (!mSideFpsEventHandlerReady.get() || !mIsSideFps.get()
+ || mFingerprintState != STATE_ENROLLING) {
+ return false;
+ }
+ mHandler.post(() -> {
+ Dialog confirmScreenOffDialog = new AlertDialog.Builder(mContext)
+ .setTitle(R.string.fp_enrollment_powerbutton_intent_title)
+ .setMessage(R.string.fp_enrollment_powerbutton_intent_message)
+ .setPositiveButton(
+ R.string.fp_enrollment_powerbutton_intent_positive_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ mPowerManager.goToSleep(
+ eventTime,
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+ 0 /* flags */
+ );
+ }
+ })
+ .setNegativeButton(
+ R.string.fp_enrollment_powerbutton_intent_negative_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .setCancelable(false)
+ .create();
+ confirmScreenOffDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+ confirmScreenOffDialog.show();
+ });
+ return true;
+ }
+
+ /**
+ * Awaits notification from PhoneWindowManager that fingerprint service is ready
+ * to send updates about power button fps sensor state. Then configures a
+ * FingerprintStateListener to receive and record updates to fps state, and
+ * registers the FingerprintStateListener in FingerprintManager.
+ */
+ public void onFingerprintSensorReady() {
+ final PackageManager pm = mContext.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) return;
+ FingerprintManager fingerprintManager =
+ mContext.getSystemService(FingerprintManager.class);
+ fingerprintManager.addAuthenticatorsRegisteredCallback(
+ new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List sensors) {
+ mIsSideFps.set(fingerprintManager.isPowerbuttonFps());
+ FingerprintStateListener fingerprintStateListener =
+ new FingerprintStateListener() {
+ @Override
+ public void onStateChanged(
+ @FingerprintStateListener.State int newState) {
+ mFingerprintState = newState;
+ }
+ };
+ fingerprintManager.registerFingerprintStateListener(
+ fingerprintStateListener);
+ mSideFpsEventHandlerReady.set(true);
+ }
+ });
+ }
+}