Fingerprint virtual HAL checkin (part 1)
- udfps and side fingerprint sensor instance
- sensor location configuration
- misc. fixes/workarounds

Bug: 230515082, 230515086
Test: atest FakeFingerprintEngineTesT
      atest FakeFingerprintEngineUdfpsTest
      atest --no-bazel-mode VtsHalBiometricsFingerprintTargetTest
Change-Id: I9bec76496b35029cab4561d0d4ea85a78bec589a
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index 2aa7bbd..dc0199c 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -16,6 +16,9 @@
     local_include_dirs: ["include"],
     srcs: [
         "FakeFingerprintEngine.cpp",
+        "FakeFingerprintEngineRear.cpp",
+        "FakeFingerprintEngineUdfps.cpp",
+        "FakeFingerprintEngineSide.cpp",
         "Fingerprint.cpp",
         "Session.cpp",
         "main.cpp",
@@ -41,6 +44,32 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
+        "android.hardware.biometrics.common.thread",
+    ],
+    static_libs: [
+        "libandroid.hardware.biometrics.fingerprint.VirtualProps",
+        "android.hardware.biometrics.fingerprint-V2-ndk",
+        "android.hardware.biometrics.common-V2-ndk",
+        "android.hardware.keymaster-V3-ndk",
+        "android.hardware.biometrics.common.util",
+    ],
+    vendor: true,
+    test_suites: ["general-tests"],
+    require_root: true,
+}
+
+cc_test {
+    name: "android.hardware.biometrics.fingerprint.FakeFingerprintEngineUdfpsTest",
+    local_include_dirs: ["include"],
+    srcs: [
+        "tests/FakeFingerprintEngineUdfpsTest.cpp",
+        "FakeFingerprintEngineUdfps.cpp",
+        "FakeFingerprintEngine.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.biometrics.common.thread",
     ],
     static_libs: [
         "libandroid.hardware.biometrics.fingerprint.VirtualProps",
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 138caa0..68a1f26 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -15,8 +15,10 @@
  */
 
 #include "FakeFingerprintEngine.h"
+#include "Fingerprint.h"
 
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 
 #include <fingerprint.sysprop.h>
 
@@ -24,6 +26,7 @@
 #include "util/Util.h"
 
 using namespace ::android::fingerprint::virt;
+using ::android::base::ParseInt;
 
 namespace aidl::android::hardware::biometrics::fingerprint {
 
@@ -172,6 +175,11 @@
     BEGIN_OP(0);
 
     std::vector<int32_t> ids;
+    // There are some enrollment sync issue with framework, which results in
+    //  a single template removal during the very firt sync command after reboot.
+    //  This is a workaround for now. TODO(b/243129174)
+    ids.push_back(-1);
+
     for (auto& enrollment : FingerprintHalProperties::enrollments()) {
         auto id = enrollment.value_or(0);
         if (id > 0) {
@@ -203,7 +211,11 @@
 
 void FakeFingerprintEngine::getAuthenticatorIdImpl(ISessionCallback* cb) {
     BEGIN_OP(0);
-    cb->onAuthenticatorIdRetrieved(FingerprintHalProperties::authenticator_id().value_or(0));
+    int64_t authenticatorId = FingerprintHalProperties::authenticator_id().value_or(0);
+    if (FingerprintHalProperties::enrollments().size() > 0 && authenticatorId == 0) {
+        authenticatorId = 99999999;  // default authenticatorId, TODO(b/230515082)
+    }
+    cb->onAuthenticatorIdRetrieved(authenticatorId);
 }
 
 void FakeFingerprintEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
@@ -221,4 +233,57 @@
     cb->onLockoutCleared();
 }
 
+ndk::ScopedAStatus FakeFingerprintEngine::onPointerDownImpl(int32_t /*pointerId*/, int32_t /*x*/,
+                                                            int32_t /*y*/, float /*minor*/,
+                                                            float /*major*/) {
+    BEGIN_OP(0);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FakeFingerprintEngine::onPointerUpImpl(int32_t /*pointerId*/) {
+    BEGIN_OP(0);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FakeFingerprintEngine::onUiReadyImpl() {
+    BEGIN_OP(0);
+    return ndk::ScopedAStatus::ok();
+}
+
+bool FakeFingerprintEngine::getSensorLocationConfig(SensorLocation& out) {
+    auto loc = FingerprintHalProperties::sensor_location().value_or("");
+    auto isValidStr = false;
+    auto dim = Util::split(loc, ":");
+
+    if (dim.size() < 3 or dim.size() > 4) {
+        if (!loc.empty()) LOG(WARNING) << "Invalid sensor location input (x:y:radius):" + loc;
+        return false;
+    } else {
+        int32_t x, y, r;
+        std::string d = "";
+        if (dim.size() >= 3) {
+            isValidStr = ParseInt(dim[0], &x) && ParseInt(dim[1], &y) && ParseInt(dim[2], &r);
+        }
+        if (dim.size() >= 4) {
+            d = dim[3];
+        }
+        if (isValidStr) out = {0, x, y, r, d};
+
+        return isValidStr;
+    }
+}
+SensorLocation FakeFingerprintEngine::getSensorLocation() {
+    SensorLocation location;
+
+    if (getSensorLocationConfig(location)) {
+        return location;
+    } else {
+        return defaultSensorLocation();
+    }
+}
+
+SensorLocation FakeFingerprintEngine::defaultSensorLocation() {
+    return {0 /* displayId (not used) */, 0 /* sensorLocationX */, 0 /* sensorLocationY */,
+            0 /* sensorRadius */, "" /* display */};
+}
 }  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineRear.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineRear.cpp
new file mode 100644
index 0000000..eea80d5
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineRear.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "FakeFingerprintEngineRear.h"
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+
+#include <fingerprint.sysprop.h>
+
+#include "util/CancellationSignal.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {}
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp
new file mode 100644
index 0000000..9f736e7
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineSide.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "FakeFingerprintEngineSide.h"
+
+#include <android-base/logging.h>
+
+#include <fingerprint.sysprop.h>
+
+#include "util/CancellationSignal.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+SensorLocation FakeFingerprintEngineSide::defaultSensorLocation() {
+    SensorLocation location;
+
+    return {0 /* displayId (not used) */, defaultSensorLocationX /* sensorLocationX */,
+            defaultSensorLocationY /* sensorLocationY */, defaultSensorRadius /* sensorRadius */,
+            "" /* display */};
+}
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
new file mode 100644
index 0000000..d8579a4
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "FakeFingerprintEngineUdfps.h"
+
+#include <android-base/logging.h>
+
+#include <fingerprint.sysprop.h>
+
+#include "util/CancellationSignal.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+SensorLocation FakeFingerprintEngineUdfps::defaultSensorLocation() {
+    return {0 /* displayId (not used) */, defaultSensorLocationX /* sensorLocationX */,
+            defaultSensorLocationY /* sensorLocationY */, defaultSensorRadius /* sensorRadius */,
+            "" /* display */};
+}
+
+ndk::ScopedAStatus FakeFingerprintEngineUdfps::onPointerDownImpl(int32_t /*pointerId*/,
+                                                                 int32_t /*x*/, int32_t /*y*/,
+                                                                 float /*minor*/, float /*major*/) {
+    BEGIN_OP(0);
+
+    // TODO(b/230515082): if need to handle display touch events
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FakeFingerprintEngineUdfps::onPointerUpImpl(int32_t /*pointerId*/) {
+    BEGIN_OP(0);
+    // TODO(b/230515082)
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus FakeFingerprintEngineUdfps::onUiReadyImpl() {
+    BEGIN_OP(0);
+    // TODO(b/230515082)
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 71dc660..922781c 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -26,7 +26,7 @@
 namespace aidl::android::hardware::biometrics::fingerprint {
 namespace {
 constexpr size_t MAX_WORKER_QUEUE_SIZE = 5;
-constexpr int SENSOR_ID = 1;
+constexpr int SENSOR_ID = 5;
 constexpr common::SensorStrength SENSOR_STRENGTH = common::SensorStrength::STRONG;
 constexpr int MAX_ENROLLMENTS_PER_USER = 5;
 constexpr bool SUPPORTS_NAVIGATION_GESTURES = true;
@@ -39,8 +39,25 @@
 
 }  // namespace
 
-Fingerprint::Fingerprint()
-    : mEngine(std::make_unique<FakeFingerprintEngine>()), mWorker(MAX_WORKER_QUEUE_SIZE) {}
+Fingerprint::Fingerprint() : mWorker(MAX_WORKER_QUEUE_SIZE) {
+    std::string sensorTypeProp = FingerprintHalProperties::type().value_or("");
+    if (sensorTypeProp == "" || sensorTypeProp == "default" || sensorTypeProp == "rear") {
+        mSensorType = FingerprintSensorType::REAR;
+        mEngine = std::make_unique<FakeFingerprintEngineRear>();
+    } else if (sensorTypeProp == "udfps") {
+        mSensorType = FingerprintSensorType::UNDER_DISPLAY_OPTICAL;
+        mEngine = std::make_unique<FakeFingerprintEngineUdfps>();
+    } else if (sensorTypeProp == "side") {
+        mSensorType = FingerprintSensorType::POWER_BUTTON;
+        mEngine = std::make_unique<FakeFingerprintEngineSide>();
+    } else {
+        mSensorType = FingerprintSensorType::UNKNOWN;
+        mEngine = std::make_unique<FakeFingerprintEngineRear>();
+        UNIMPLEMENTED(FATAL) << "unrecognized or unimplemented fingerprint behavior: "
+                             << sensorTypeProp;
+    }
+    LOG(INFO) << "sensorTypeProp:" << sensorTypeProp;
+}
 
 ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* out) {
     std::vector<common::ComponentInfo> componentInfo = {
@@ -51,22 +68,12 @@
     common::CommonProps commonProps = {SENSOR_ID, SENSOR_STRENGTH, MAX_ENROLLMENTS_PER_USER,
                                        componentInfo};
 
-    SensorLocation sensorLocation = {0 /* displayId (not used) */, 0 /* sensorLocationX */,
-                                     0 /* sensorLocationY */, 0 /* sensorRadius */,
-                                     "" /* display */};
+    SensorLocation sensorLocation = mEngine->getSensorLocation();
 
-    FingerprintSensorType sensorType = FingerprintSensorType::UNKNOWN;
-    std::string sensorTypeProp = FingerprintHalProperties::type().value_or("");
-    if (sensorTypeProp == "" || sensorTypeProp == "default" || sensorTypeProp == "rear") {
-        sensorType = FingerprintSensorType::REAR;
-    }
-    if (sensorType == FingerprintSensorType::UNKNOWN) {
-        UNIMPLEMENTED(FATAL) << "unrecognized or unimplemented fingerprint behavior: "
-                             << sensorTypeProp;
-    }
+    LOG(INFO) << "sensor type:" << (int)mSensorType << " location:" << sensorLocation.toString();
 
     *out = {{commonProps,
-             sensorType,
+             mSensorType,
              {sensorLocation},
              SUPPORTS_NAVIGATION_GESTURES,
              false /* supportsDetectInteraction */}};
@@ -80,6 +87,8 @@
 
     mSession = SharedRefBase::make<Session>(sensorId, userId, cb, mEngine.get(), &mWorker);
     *out = mSession;
+
+    LOG(INFO) << "createSession: sensorId:" << sensorId << " userId:" << userId;
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md
index a6e6b81..bb6bb48 100644
--- a/biometrics/fingerprint/aidl/default/README.md
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -23,37 +23,17 @@
 First, set the type of sensor the device should use, enable the virtual
 extensions in the framework, and reboot.
 
-This doesn't work with HIDL and you typically need to have a PIN or password set
-for things to work correctly, so this is a good time to set those too.
-
 ```shell
 $ adb root
 $ adb shell settings put secure biometric_virtual_enabled 1
 $ adb shell setprop persist.vendor.fingerprint.virtual.type rear
-$ adb shell locksettings set-pin 0000
-$ adb shell settings put secure com.android.server.biometrics.AuthService.hidlDisabled 1
 $ adb reboot
 ```
 
 ### Enrollments
 
 Next, setup enrollments on the device. This can either be done through the UI,
-or via adb.
-
-#### UI Enrollment
-
-1. Tee up the results of the enrollment before starting the process:
-
-      ```shell
-      $ adb shell setprop vendor.fingerprint.virtual.next_enrollment 1:100,100,100:true
-      ```
-2. Navigate to `Settings -> Security -> Fingerprint Unlock` and follow the
-   prompts.
-3. Verify the enrollments in the UI:
-
-      ```shell
-      $ adb shell getprop persist.vendor.fingerprint.virtual.enrollments
-      ```
+or via adb directly.
 
 #### Direct Enrollment
 
@@ -61,14 +41,29 @@
 
 ```shell
 $ adb root
+$ adb shell locksettings set-pin 0000
 $ adb shell setprop persist.vendor.fingerprint.virtual.enrollments 1
 $ adb shell cmd fingerprint sync
 ```
 
-**Note: You may need to do this twice.** The templates are checked as part of
-some lazy operations, like user switching and startup, which can cause the
-framework to delete the enrollments before the sync operation runs. Until this
-is fixed, just run the commands twice as a workaround.
+#### UI Enrollment
+
+1. Set pin
+      ```shell
+      $ adb shell locksettings set-pin 0000
+      ```
+2. Tee up the results of the enrollment before starting the process:
+
+      ```shell
+      $ adb shell setprop vendor.fingerprint.virtual.next_enrollment 1:100,100,100:true
+      ```
+3. Navigate to `Settings -> Security -> Fingerprint Unlock` and follow the
+   prompts.
+4. Verify the enrollments in the UI:
+
+      ```shell
+      $ adb shell getprop persist.vendor.fingerprint.virtual.enrollments
+      ```
 
 ### Authenticate
 
@@ -81,7 +76,7 @@
 
 ### View HAL State
 
-To view all the properties of the HAL (see `fingerprint.sysprop` for the API):
+To view all the properties of the HAL (see `fingerprint.sysprop` file for the API):
 
 ```shell
 $ adb shell getprop | grep vendor.fingerprint.virtual
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index ab91e98..e51f677 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -167,7 +167,7 @@
 }
 
 ndk::ScopedAStatus Session::removeEnrollments(const std::vector<int32_t>& enrollmentIds) {
-    LOG(INFO) << "removeEnrollments";
+    LOG(INFO) << "removeEnrollments, size:" << enrollmentIds.size();
     scheduleStateOrCrash(SessionState::REMOVING_ENROLLMENTS);
 
     mWorker->schedule(Callable::from([this, enrollmentIds] {
@@ -228,19 +228,31 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Session::onPointerDown(int32_t /*pointerId*/, int32_t /*x*/, int32_t /*y*/,
-                                          float /*minor*/, float /*major*/) {
+ndk::ScopedAStatus Session::onPointerDown(int32_t pointerId, int32_t x, int32_t y, float minor,
+                                          float major) {
     LOG(INFO) << "onPointerDown";
+    mWorker->schedule(Callable::from([this, pointerId, x, y, minor, major] {
+        mEngine->onPointerDownImpl(pointerId, x, y, minor, major);
+        enterIdling();
+    }));
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Session::onPointerUp(int32_t /*pointerId*/) {
+ndk::ScopedAStatus Session::onPointerUp(int32_t pointerId) {
     LOG(INFO) << "onPointerUp";
+    mWorker->schedule(Callable::from([this, pointerId] {
+        mEngine->onPointerUpImpl(pointerId);
+        enterIdling();
+    }));
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Session::onUiReady() {
     LOG(INFO) << "onUiReady";
+    mWorker->schedule(Callable::from([this] {
+        mEngine->onUiReadyImpl();
+        enterIdling();
+    }));
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
index 4724ff4..9dfb74d 100644
--- a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
+++ b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt
@@ -76,6 +76,12 @@
     prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
   }
   prop {
+    api_name: "sensor_location"
+    type: String
+    access: ReadWrite
+    prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
+  }
+  prop {
     api_name: "type"
     type: String
     access: ReadWrite
diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
index 12c8648..85e93b0 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -133,3 +133,13 @@
     access: ReadWrite
     api_name: "operation_authenticate_duration"
 }
+
+# sensor location
+#    <x>:<y>:<radius> in pixel
+prop {
+    prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
+    type: String
+    scope: Public
+    access: ReadWrite
+    api_name: "sensor_location"
+}
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index eb810da..d7df818 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -20,6 +20,7 @@
 
 #include <random>
 
+#include <aidl/android/hardware/biometrics/fingerprint/SensorLocation.h>
 #include <future>
 #include <vector>
 
@@ -31,6 +32,7 @@
 class FakeFingerprintEngine {
   public:
     FakeFingerprintEngine() : mRandom(std::mt19937::default_seed) {}
+    virtual ~FakeFingerprintEngine() {}
 
     void generateChallengeImpl(ISessionCallback* cb);
     void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge);
@@ -44,6 +46,18 @@
     void getAuthenticatorIdImpl(ISessionCallback* cb);
     void invalidateAuthenticatorIdImpl(ISessionCallback* cb);
     void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/);
+    bool getSensorLocationConfig(SensorLocation& out);
+
+    virtual ndk::ScopedAStatus onPointerDownImpl(int32_t pointerId, int32_t x, int32_t y,
+                                                 float minor, float major);
+
+    virtual ndk::ScopedAStatus onPointerUpImpl(int32_t pointerId);
+
+    virtual ndk::ScopedAStatus onUiReadyImpl();
+
+    virtual SensorLocation getSensorLocation();
+
+    virtual SensorLocation defaultSensorLocation();
 
     std::mt19937 mRandom;
 };
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
new file mode 100644
index 0000000..1600a4b
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#pragma once
+#include "FakeFingerprintEngine.h"
+
+using namespace ::aidl::android::hardware::biometrics::common;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+// A fake engine that is backed by system properties instead of hardware.
+class FakeFingerprintEngineRear : public FakeFingerprintEngine {
+  public:
+    FakeFingerprintEngineRear() : FakeFingerprintEngine() {}
+    ~FakeFingerprintEngineRear() {}
+};
+
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
new file mode 100644
index 0000000..4e44d16
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#pragma once
+#include "FakeFingerprintEngine.h"
+
+using namespace ::aidl::android::hardware::biometrics::common;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+// A fake engine that is backed by system properties instead of hardware.
+class FakeFingerprintEngineSide : public FakeFingerprintEngine {
+  public:
+    static constexpr int32_t defaultSensorLocationX = 0;
+    static constexpr int32_t defaultSensorLocationY = 600;
+    static constexpr int32_t defaultSensorRadius = 150;
+
+    FakeFingerprintEngineSide() : FakeFingerprintEngine() {}
+    ~FakeFingerprintEngineSide() {}
+
+    virtual SensorLocation defaultSensorLocation() override;
+};
+
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
new file mode 100644
index 0000000..b86af73
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+#include "FakeFingerprintEngine.h"
+
+using namespace ::aidl::android::hardware::biometrics::common;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+// A fake engine that is backed by system properties instead of hardware.
+class FakeFingerprintEngineUdfps : public FakeFingerprintEngine {
+  public:
+    static constexpr int32_t defaultSensorLocationX = 400;
+    static constexpr int32_t defaultSensorLocationY = 1600;
+    static constexpr int32_t defaultSensorRadius = 150;
+
+    FakeFingerprintEngineUdfps() : FakeFingerprintEngine() {}
+    ~FakeFingerprintEngineUdfps() {}
+
+    virtual ndk::ScopedAStatus onPointerDownImpl(int32_t pointerId, int32_t x, int32_t y,
+                                                 float minor, float major) override;
+
+    virtual ndk::ScopedAStatus onPointerUpImpl(int32_t pointerId) override;
+
+    virtual ndk::ScopedAStatus onUiReadyImpl() override;
+
+    virtual SensorLocation defaultSensorLocation() override;
+};
+
+}  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/Fingerprint.h b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
index 20def0c..64aafa3 100644
--- a/biometrics/fingerprint/aidl/default/include/Fingerprint.h
+++ b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
@@ -16,9 +16,15 @@
 
 #pragma once
 
+#define LOG_TAG "FingerprintVirtualHal"
+
 #include <aidl/android/hardware/biometrics/fingerprint/BnFingerprint.h>
 
 #include "FakeFingerprintEngine.h"
+#include "FakeFingerprintEngineRear.h"
+#include "FakeFingerprintEngineSide.h"
+#include "FakeFingerprintEngineUdfps.h"
+
 #include "Session.h"
 #include "thread/WorkerThread.h"
 
@@ -38,6 +44,7 @@
     std::unique_ptr<FakeFingerprintEngine> mEngine;
     WorkerThread mWorker;
     std::shared_ptr<Session> mSession;
+    FingerprintSensorType mSensorType;
 };
 
 }  // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
index 742d933..8696d26 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -264,7 +264,9 @@
 TEST_F(FakeFingerprintEngineTest, EnumerateEnrolled) {
     FingerprintHalProperties::enrollments({2, 4, 8});
     mEngine.enumerateEnrollmentsImpl(mCallback.get());
-    ASSERT_EQ(3, mCallback->mLastEnrollmentEnumerated.size());
+    ASSERT_EQ(
+            4,
+            mCallback->mLastEnrollmentEnumerated.size());  // Due to workaround. TODO (b/243129174)
     for (auto id : FingerprintHalProperties::enrollments()) {
         ASSERT_TRUE(std::find(mCallback->mLastEnrollmentEnumerated.begin(),
                               mCallback->mLastEnrollmentEnumerated.end(),
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
new file mode 100644
index 0000000..485f401
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <android/binder_process.h>
+#include <fingerprint.sysprop.h>
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include "FakeFingerprintEngine.h"
+#include "FakeFingerprintEngineUdfps.h"
+
+using namespace ::android::fingerprint::virt;
+using namespace ::aidl::android::hardware::biometrics::fingerprint;
+using namespace ::aidl::android::hardware::keymaster;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class FakeFingerprintEngineUdfpsTest : public ::testing::Test {
+  protected:
+    void SetUp() override {}
+
+    void TearDown() override {
+        // reset to default
+        FingerprintHalProperties::sensor_location("");
+    }
+
+    FakeFingerprintEngineUdfps mEngine;
+};
+
+bool isDefaultLocation(SensorLocation& sc) {
+    return (sc.sensorLocationX == FakeFingerprintEngineUdfps::defaultSensorLocationX &&
+            sc.sensorLocationY == FakeFingerprintEngineUdfps::defaultSensorLocationY &&
+            sc.sensorRadius == FakeFingerprintEngineUdfps::defaultSensorRadius && sc.display == "");
+}
+
+TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocation) {
+    FingerprintHalProperties::sensor_location("");
+    SensorLocation sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+
+    auto loc = "100:200:30";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(sc.sensorLocationX == 100);
+    ASSERT_TRUE(sc.sensorLocationY == 200);
+    ASSERT_TRUE(sc.sensorRadius == 30);
+
+    loc = "100:200:30:screen1";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(sc.sensorLocationX == 100);
+    ASSERT_TRUE(sc.sensorLocationY == 200);
+    ASSERT_TRUE(sc.sensorRadius == 30);
+    ASSERT_TRUE(sc.display == "screen1");
+
+    loc = "100";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+
+    loc = "10:20";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+
+    loc = "10,20,5";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+
+    loc = "a:b:c";
+    FingerprintHalProperties::sensor_location(loc);
+    sc = mEngine.getSensorLocation();
+    ASSERT_TRUE(isDefaultLocation(sc));
+}
+
+// More
+}  // namespace aidl::android::hardware::biometrics::fingerprint