1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.biometrics 18 19 import android.content.Context 20 import android.graphics.Rect 21 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP 22 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD 23 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER 24 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS 25 import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING 26 import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR 27 import android.hardware.biometrics.BiometricRequestConstants.REASON_UNKNOWN 28 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback 29 import android.util.Log 30 import android.view.LayoutInflater 31 import android.view.MotionEvent 32 import android.view.MotionEvent.ACTION_DOWN 33 import android.view.MotionEvent.ACTION_MOVE 34 import android.view.MotionEvent.ACTION_UP 35 import com.android.internal.annotations.VisibleForTesting 36 import com.android.systemui.dagger.SysUISingleton 37 import com.android.systemui.statusbar.commandline.Command 38 import com.android.systemui.statusbar.commandline.CommandRegistry 39 import java.io.PrintWriter 40 import javax.inject.Inject 41 42 private const val TAG = "UdfpsShell" 43 private const val REQUEST_ID = 2L 44 private const val SENSOR_ID = 0 45 private const val MINOR = 10F 46 private const val MAJOR = 10F 47 48 /** Used to show and hide the UDFPS overlay with statusbar commands. */ 49 @SysUISingleton 50 class UdfpsShell @Inject constructor(commandRegistry: CommandRegistry) : Command { 51 52 /** 53 * Set in [UdfpsController.java] constructor, used to show and hide the UDFPS overlay. 54 * 55 * TODO: inject after b/229290039 is resolved 56 */ 57 var udfpsOverlayController: UdfpsController.UdfpsOverlayController? = null 58 var context: Context? = null 59 var inflater: LayoutInflater? = null 60 61 init { <lambda>null62 commandRegistry.registerCommand("udfps") { this } 63 } 64 executenull65 override fun execute(pw: PrintWriter, args: List<String>) { 66 if (args.size == 1 && args[0] == "hide") { 67 hideOverlay() 68 } else if (args.size == 2 && args[0] == "show") { 69 showOverlay(getEnrollmentReason(args[1])) 70 } else if (args.size == 1 && args[0] == "onUiReady") { 71 onUiReady() 72 } else if (args.size == 1 && args[0] == "simFingerDown") { 73 simFingerDown() 74 } else if (args.size == 1 && args[0] == "simFingerUp") { 75 simFingerUp() 76 } else if (args.size == 1 && args[0] == "biometricPrompt") { 77 launchBiometricPrompt() 78 } else if (args.size == 2 && args[0] == "setIgnoreDisplayTouches") { 79 setIgnoreDisplayTouches(args[1].toBoolean()) 80 } else { 81 invalidCommand(pw) 82 } 83 } 84 helpnull85 override fun help(pw: PrintWriter) { 86 pw.println("Usage: adb shell cmd statusbar udfps <cmd>") 87 pw.println("Supported commands:") 88 pw.println(" - show <reason>") 89 pw.println( 90 " -> supported reasons: [enroll-find-sensor, enroll-enrolling, auth-bp, " + 91 "auth-keyguard, auth-other, auth-settings]" 92 ) 93 pw.println(" -> reason otherwise defaults to unknown") 94 pw.println(" - hide") 95 pw.println(" - onUiReady") 96 pw.println(" - simFingerDown") 97 pw.println(" -> Simulates onFingerDown on sensor") 98 pw.println(" - simFingerUp") 99 pw.println(" -> Simulates onFingerUp on sensor") 100 pw.println(" - biometricPrompt") 101 pw.println(" -> Shows Biometric Prompt") 102 } 103 invalidCommandnull104 private fun invalidCommand(pw: PrintWriter) { 105 pw.println("invalid command") 106 help(pw) 107 } 108 getEnrollmentReasonnull109 private fun getEnrollmentReason(reason: String): Int { 110 return when (reason) { 111 "enroll-find-sensor" -> REASON_ENROLL_FIND_SENSOR 112 "enroll-enrolling" -> REASON_ENROLL_ENROLLING 113 "auth-bp" -> REASON_AUTH_BP 114 "auth-keyguard" -> REASON_AUTH_KEYGUARD 115 "auth-other" -> REASON_AUTH_OTHER 116 "auth-settings" -> REASON_AUTH_SETTINGS 117 else -> REASON_UNKNOWN 118 } 119 } 120 showOverlaynull121 private fun showOverlay(reason: Int) { 122 udfpsOverlayController?.showUdfpsOverlay( 123 REQUEST_ID, 124 SENSOR_ID, 125 reason, 126 object : IUdfpsOverlayControllerCallback.Stub() { 127 override fun onUserCanceled() { 128 Log.e(TAG, "User cancelled") 129 } 130 } 131 ) 132 } 133 hideOverlaynull134 private fun hideOverlay() { 135 udfpsOverlayController?.hideUdfpsOverlay(SENSOR_ID) 136 } 137 launchBiometricPromptnull138 private fun launchBiometricPrompt() { 139 udfpsOverlayController?.debugBiometricPrompt() 140 } 141 142 @VisibleForTesting onUiReadynull143 fun onUiReady() { 144 udfpsOverlayController?.debugOnUiReady(SENSOR_ID) 145 } 146 147 @VisibleForTesting simFingerDownnull148 fun simFingerDown() { 149 val sensorBounds: Rect = udfpsOverlayController!!.sensorBounds 150 151 val downEvent: MotionEvent? = 152 obtainMotionEvent( 153 ACTION_DOWN, 154 sensorBounds.exactCenterX(), 155 sensorBounds.exactCenterY(), 156 MINOR, 157 MAJOR 158 ) 159 udfpsOverlayController?.debugOnTouch(downEvent) 160 161 val moveEvent: MotionEvent? = 162 obtainMotionEvent( 163 ACTION_MOVE, 164 sensorBounds.exactCenterX(), 165 sensorBounds.exactCenterY(), 166 MINOR, 167 MAJOR 168 ) 169 udfpsOverlayController?.debugOnTouch(moveEvent) 170 171 downEvent?.recycle() 172 moveEvent?.recycle() 173 } 174 175 @VisibleForTesting simFingerUpnull176 fun simFingerUp() { 177 val sensorBounds: Rect = udfpsOverlayController!!.sensorBounds 178 179 val upEvent: MotionEvent? = 180 obtainMotionEvent( 181 ACTION_UP, 182 sensorBounds.exactCenterX(), 183 sensorBounds.exactCenterY(), 184 MINOR, 185 MAJOR 186 ) 187 udfpsOverlayController?.debugOnTouch(upEvent) 188 upEvent?.recycle() 189 } 190 191 @VisibleForTesting setIgnoreDisplayTouchesnull192 fun setIgnoreDisplayTouches(ignoreTouches: Boolean) { 193 udfpsOverlayController?.debugSetIgnoreDisplayTouches(ignoreTouches) 194 } 195 obtainMotionEventnull196 private fun obtainMotionEvent( 197 action: Int, 198 x: Float, 199 y: Float, 200 minor: Float, 201 major: Float 202 ): MotionEvent? { 203 val pp = MotionEvent.PointerProperties() 204 pp.id = 1 205 val pc = MotionEvent.PointerCoords() 206 pc.x = x 207 pc.y = y 208 pc.touchMinor = minor 209 pc.touchMajor = major 210 return MotionEvent.obtain( 211 0, 212 0, 213 action, 214 1, 215 arrayOf(pp), 216 arrayOf(pc), 217 0, 218 0, 219 1f, 220 1f, 221 0, 222 0, 223 0, 224 0 225 ) 226 } 227 } 228