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

Commit 56f6d508 authored by Beverly Tai's avatar Beverly Tai Committed by Automerger Merge Worker
Browse files

Merge "Add adb commands to test udfps haptics" into sc-dev am: e74e0ce2

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15099868

Change-Id: Ic07eee65ed270fc9afac7a5cb2c1d5f5c3e13fb6
parents 57f1e11d e74e0ce2
Loading
Loading
Loading
Loading
+19 −14
Original line number Diff line number Diff line
@@ -1369,7 +1369,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                    Trace.endSection();

                    // on auth success, we sometimes never received an acquired haptic
                    if (!mPlayedAcquiredHaptic) {
                    if (!mPlayedAcquiredHaptic && isUdfpsEnrolled()) {
                        playAcquiredHaptic();
                    }
                }
@@ -1387,7 +1387,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                @Override
                public void onAuthenticationAcquired(int acquireInfo) {
                    handleFingerprintAcquired(acquireInfo);
                    if (acquireInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
                    if (acquireInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD
                            && isUdfpsEnrolled()) {
                        mPlayedAcquiredHaptic = true;
                        playAcquiredHaptic();
                    }
                }
@@ -1402,10 +1404,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                public void onUdfpsPointerUp(int sensorId) {
                    Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId);
                }
            };

                private void playAcquiredHaptic() {
                    if (mAcquiredHapticEnabled && mVibrator != null && isUdfpsEnrolled()) {
                        mPlayedAcquiredHaptic = true;
    /**
     * Play haptic to signal udfps fingeprrint acquired.
     */
    @VisibleForTesting
    public void playAcquiredHaptic() {
        if (mAcquiredHapticEnabled && mVibrator != null) {
            String effect = Settings.Global.getString(
                    mContext.getContentResolver(),
                    "udfps_acquired_type");
@@ -1414,7 +1420,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                    UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES);
        }
    }
            };

    private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
            = (sensorId, userId, isStrongBiometric) -> {
+25 −16
Original line number Diff line number Diff line
@@ -430,21 +430,7 @@ public class UdfpsController implements DozeReceiver {
                            mTouchLogTime = SystemClock.elapsedRealtime();
                            mPowerManager.userActivity(SystemClock.uptimeMillis(),
                                    PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);

                            // TODO: this should eventually be removed after ux testing
                            if (mVibrator != null) {
                                final ContentResolver contentResolver =
                                        mContext.getContentResolver();
                                int startEnabled = Settings.Global.getInt(contentResolver,
                                        "udfps_start", 1);
                                if (startEnabled > 0) {
                                    String startEffectSetting = Settings.Global.getString(
                                            contentResolver, "udfps_start_type");
                                    mVibrator.vibrate(getVibration(startEffectSetting,
                                            EFFECT_CLICK), VIBRATION_SONIFICATION_ATTRIBUTES);
                                }
                            }

                            playStartHaptic();
                            handled = true;
                        } else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
                            Log.v(TAG, "onTouch | finger move: " + touchInfo);
@@ -498,6 +484,7 @@ public class UdfpsController implements DozeReceiver {
            @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
            @NonNull ScreenLifecycle screenLifecycle,
            @Nullable Vibrator vibrator,
            @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
            @NonNull Optional<UdfpsHbmProvider> hbmProvider) {
        mContext = context;
        mExecution = execution;
@@ -544,6 +531,29 @@ public class UdfpsController implements DozeReceiver {
        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        context.registerReceiver(mBroadcastReceiver, filter);

        udfpsHapticsSimulator.setUdfpsController(this);
    }

    /**
     * Play haptic to signal udfps scanning started.
     */
    @VisibleForTesting
    public void playStartHaptic() {
        if (mVibrator != null) {
            final ContentResolver contentResolver =
                    mContext.getContentResolver();
            // TODO: these settings checks should eventually be removed after ux testing
            //  (b/185124905)
            int startEnabled = Settings.Global.getInt(contentResolver,
                    "udfps_start", 1);
            if (startEnabled > 0) {
                String startEffectSetting = Settings.Global.getString(
                        contentResolver, "udfps_start_type");
                mVibrator.vibrate(getVibration(startEffectSetting,
                        EFFECT_CLICK), VIBRATION_SONIFICATION_ATTRIBUTES);
            }
        }
    }

    private int getCoreLayoutParamFlags() {
@@ -836,7 +846,6 @@ public class UdfpsController implements DozeReceiver {
        }
    }


    /**
     * get vibration to play given string
     * used for testing purposes (b/185124905)
+94 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.biometrics

import android.media.AudioAttributes
import android.os.VibrationEffect
import android.os.Vibrator

import com.android.keyguard.KeyguardUpdateMonitor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry

import java.io.PrintWriter

import javax.inject.Inject

/**
 * Used to simulate haptics that may be used for udfps authentication.
 */
@SysUISingleton
class UdfpsHapticsSimulator @Inject constructor(
    commandRegistry: CommandRegistry,
    val vibrator: Vibrator?,
    val keyguardUpdateMonitor: KeyguardUpdateMonitor
) : Command {
    val sonificationEffects =
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
            .build()
    var udfpsController: UdfpsController? = null

    init {
        commandRegistry.registerCommand("udfps-haptic") { this }
    }

    override fun execute(pw: PrintWriter, args: List<String>) {
        if (args.isEmpty()) {
            invalidCommand(pw)
        } else {
            when (args[0]) {
                "start" -> {
                    udfpsController?.playStartHaptic()
                }
                "acquired" -> {
                    keyguardUpdateMonitor.playAcquiredHaptic()
                }
                "success" -> {
                    // needs to be kept up to date with AcquisitionClient#SUCCESS_VIBRATION_EFFECT
                    vibrator?.vibrate(
                        VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
                        sonificationEffects)
                }
                "error" -> {
                    // needs to be kept up to date with AcquisitionClient#ERROR_VIBRATION_EFFECT
                    vibrator?.vibrate(
                        VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
                        sonificationEffects)
                }
                else -> invalidCommand(pw)
            }
        }
    }

    override fun help(pw: PrintWriter) {
        pw.println("Usage: adb shell cmd statusbar udfps-haptic <haptic>")
        pw.println("Available commands:")
        pw.println("  start")
        pw.println("  acquired")
        pw.println("  success, always plays CLICK haptic")
        pw.println("  error, always plays DOUBLE_CLICK haptic")
    }

    fun invalidCommand(pw: PrintWriter) {
        pw.println("invalid command")
        help(pw)
    }
}
 No newline at end of file
+3 −0
Original line number Diff line number Diff line
@@ -126,6 +126,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
    private ScreenLifecycle mScreenLifecycle;
    @Mock
    private Vibrator mVibrator;
    @Mock
    private UdfpsHapticsSimulator mUdfpsHapticsSimulator;

    private FakeExecutor mFgExecutor;

@@ -188,6 +190,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
                mLockscreenShadeTransitionController,
                mScreenLifecycle,
                mVibrator,
                mUdfpsHapticsSimulator,
                Optional.of(mHbmProvider));
        verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
        mOverlayController = mOverlayCaptor.getValue();
+18 −46
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.biometrics.sensors;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.media.AudioAttributes;
@@ -27,7 +26,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.TextUtils;
import android.util.Slog;

/**
@@ -39,24 +37,18 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement

    private static final String TAG = "Biometrics/AcquisitionClient";

    private static final AudioAttributes VIBRATION_SONFICATION_ATTRIBUTES =
    private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
            new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                    .build();

    private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK);
    private final VibrationEffect mEffectTextureTick =
            VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
    private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
    private final VibrationEffect mEffectHeavy =
            VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
    private final VibrationEffect mDoubleClick =
    private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
            VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
    private static final VibrationEffect ERROR_VIBRATION_EFFECT =
            VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);

    private final PowerManager mPowerManager;
    private final VibrationEffect mSuccessVibrationEffect;
    private final VibrationEffect mErrorVibrationEffect;
    private boolean mShouldSendErrorToClient = true;
    private boolean mAlreadyCancelled;

@@ -72,8 +64,6 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
        super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality,
                statsAction, statsClient);
        mPowerManager = context.getSystemService(PowerManager.class);
        mSuccessVibrationEffect = mEffectClick;
        mErrorVibrationEffect = mDoubleClick;
    }

    @Override
@@ -192,49 +182,31 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
        mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
    }

    protected @Nullable VibrationEffect getSuccessVibrationEffect() {
        return mSuccessVibrationEffect;
    protected boolean successHapticsEnabled() {
        return true;
    }

    protected @Nullable VibrationEffect getErrorVibrationEffect() {
        return mErrorVibrationEffect;
    protected boolean errorHapticsEnabled() {
        return true;
    }

    protected final void vibrateSuccess() {
        if (!successHapticsEnabled()) {
            return;
        }
        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
        VibrationEffect effect = getSuccessVibrationEffect();
        if (vibrator != null && effect != null) {
            vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES);
        if (vibrator != null) {
            vibrator.vibrate(SUCCESS_VIBRATION_EFFECT, VIBRATION_SONIFICATION_ATTRIBUTES);
        }
    }

    protected final void vibrateError() {
        if (!errorHapticsEnabled()) {
            return;
        }
        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
        VibrationEffect effect = getErrorVibrationEffect();
        if (vibrator != null && effect != null) {
            vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES);
        }
    }

    protected final @NonNull VibrationEffect getVibration(@Nullable String effect,
            @NonNull VibrationEffect defaultEffect) {
        if (TextUtils.isEmpty(effect)) {
            return defaultEffect;
        }

        switch (effect.toLowerCase()) {
            case "click":
                return mEffectClick;
            case "heavy":
                return mEffectHeavy;
            case "texture_tick":
                return mEffectTextureTick;
            case "tick":
                return mEffectTick;
            case "double_click":
                return mDoubleClick;
            default:
                return defaultEffect;
        if (vibrator != null) {
            vibrator.vibrate(ERROR_VIBRATION_EFFECT, VIBRATION_SONIFICATION_ATTRIBUTES);
        }
    }
}
Loading