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

Commit 67250948 authored by Jeff Pu's avatar Jeff Pu Committed by Android (Google) Code Review
Browse files

Merge "Face Biometric Virtual HAL Authentication Implementation" into main

parents b4c44193 484d2e70
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@
#include <thread>
#include <vector>

#include <android-base/parseint.h>
using ::android::base::ParseInt;

namespace aidl::android::hardware::biometrics {

#define SLEEP_MS(x) \
@@ -64,6 +67,87 @@ class Util {
                std::sregex_token_iterator());
        return parts;
    }

    // Returns a vector of integers for the string separated by comma,
    // Empty vector is returned if there is any parsing error
    static std::vector<int32_t> parseIntSequence(const std::string& str,
                                                 const std::string& sep = ",") {
        std::vector<std::string> seqs = Util::split(str, sep);
        std::vector<int32_t> res;

        for (const auto& seq : seqs) {
            int32_t val;
            if (ParseInt(seq, &val)) {
                res.push_back(val);
            } else {
                LOG(WARNING) << "Invalid int sequence:" + str + " seq:" + seq;
                res.clear();
                break;
            }
        }

        return res;
    }

    // Parses a single enrollment stage string in the format of
    //     enroll_stage_spec: <duration>[-acquiredInfos]
    //                                      duration: integerInMs
    //                                      acquiredInfos: [info1,info2,...]
    //
    // Returns false if there is parsing error
    //
    static bool parseEnrollmentCaptureSingle(const std::string& str,
                                             std::vector<std::vector<int32_t>>& res) {
        std::vector<int32_t> defaultAcquiredInfo = {1};
        bool aborted = true;

        do {
            std::smatch sms;
            // Parses strings like "1000-[5,1]" or "500"
            std::regex ex("((\\d+)(-\\[([\\d|,]+)\\])?)");
            if (!regex_match(str.cbegin(), str.cend(), sms, ex)) break;
            int32_t duration;
            if (!ParseInt(sms.str(2), &duration)) break;
            res.push_back({duration});
            if (!sms.str(4).empty()) {
                auto acqv = parseIntSequence(sms.str(4));
                if (acqv.empty()) break;
                res.push_back(acqv);
            } else
                res.push_back(defaultAcquiredInfo);
            aborted = false;
        } while (0);

        return !aborted;
    }

    // Parses enrollment string consisting of one or more stages in the formst of
    //  <enroll_stage_spec>[,enroll_stage_spec,...]
    // Empty vector is returned in case of parsing error
    static std::vector<std::vector<int32_t>> parseEnrollmentCapture(const std::string& str) {
        std::vector<std::vector<int32_t>> res;

        std::string s(str);
        s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
        bool aborted = false;
        std::smatch sms;
        // Parses strings like "1000-[5,1],500,800-[6,5,1]"
        //                               -------------- ----- ---------------
        //  into parts:                       A       B       C
        while (regex_search(s, sms, std::regex("^(,)?(\\d+(-\\[[\\d|,]+\\])?)"))) {
            if (!parseEnrollmentCaptureSingle(sms.str(2), res)) {
                aborted = true;
                break;
            }
            s = sms.suffix();
        }
        if (aborted || s.length() != 0) {
            res.clear();
            LOG(ERROR) << "Failed to parse enrollment captures:" + str;
        }

        return res;
    }
};

}  // namespace aidl::android::hardware::biometrics
+110 −39
Original line number Diff line number Diff line
@@ -136,32 +136,52 @@ void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationI
                                      const std::future<void>& cancel) {
    BEGIN_OP(FaceHalProperties::operation_authenticate_latency().value_or(0));

    // Signal to the framework that we have begun authenticating.
    AuthenticationFrame frame;
    frame.data.acquiredInfo = AcquiredInfo::START;
    frame.data.vendorCode = 0;
    cb->onAuthenticationFrame(frame);

    // Also signal that we have opened the camera.
    frame = {};
    frame.data.acquiredInfo = AcquiredInfo::FIRST_FRAME_RECEIVED;
    frame.data.vendorCode = 0;
    cb->onAuthenticationFrame(frame);
    auto id = FaceHalProperties::enrollment_hit().value_or(0);
    auto enrolls = FaceHalProperties::enrollments();
    auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();

    auto now = Util::getSystemNanoTime();
    int64_t duration = FaceHalProperties::operation_authenticate_duration().value_or(0);
    if (duration > 0) {
        do {
            SLEEP_MS(5);
        } while (!Util::hasElapsed(now, duration));
    auto vec2str = [](std::vector<AcquiredInfo> va) {
        std::stringstream ss;
        bool isFirst = true;
        for (auto ac : va) {
            if (!isFirst) ss << ",";
            ss << std::to_string((int8_t)ac);
            isFirst = false;
        }
        return ss.str();
    };

    // default behavior mimic face sensor in U
    int64_t defaultAuthDuration = 500;
    std::string defaultAcquiredInfo =
            vec2str({AcquiredInfo::START, AcquiredInfo::FIRST_FRAME_RECEIVED});
    if (!isEnrolled) {
        std::vector<AcquiredInfo> v;
        for (int i = 0; i < 56; i++) v.push_back(AcquiredInfo::NOT_DETECTED);
        defaultAcquiredInfo += "," + vec2str(v);
        defaultAuthDuration = 2100;
    } else {
        defaultAcquiredInfo += "," + vec2str({AcquiredInfo::TOO_BRIGHT, AcquiredInfo::TOO_BRIGHT,
                                              AcquiredInfo::TOO_BRIGHT, AcquiredInfo::TOO_BRIGHT,
                                              AcquiredInfo::GOOD, AcquiredInfo::GOOD});
    }

    if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
        LOG(ERROR) << "Fail: operation_authenticate_fails";
        cb->onError(Error::VENDOR, 0 /* vendorError */);
    int64_t now = Util::getSystemNanoTime();
    int64_t duration =
            FaceHalProperties::operation_authenticate_duration().value_or(defaultAuthDuration);
    auto acquired =
            FaceHalProperties::operation_authenticate_acquired().value_or(defaultAcquiredInfo);
    auto acquiredInfos = Util::parseIntSequence(acquired);
    int N = acquiredInfos.size();

    if (N == 0) {
        LOG(ERROR) << "Fail to parse authentiate acquired info: " + acquired;
        cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
        return;
    }

    int i = 0;
    do {
        if (FaceHalProperties::lockout().value_or(false)) {
            LOG(ERROR) << "Fail: lockout";
            cb->onLockoutPermanent();
@@ -169,23 +189,74 @@ void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationI
            return;
        }

        if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
            LOG(ERROR) << "Fail: operation_authenticate_fails";
            cb->onAuthenticationFailed();
            return;
        }

        auto err = FaceHalProperties::operation_authenticate_error().value_or(0);
        if (err != 0) {
            LOG(ERROR) << "Fail: operation_authenticate_error";
            auto ec = convertError(err);
            cb->onError(ec.first, ec.second);
            return; /* simply terminating current operation for any user inserted error,
                            revisit if tests need*/
        }

        if (shouldCancel(cancel)) {
            LOG(ERROR) << "Fail: cancel";
            cb->onError(Error::CANCELED, 0 /* vendorCode */);
            return;
        }

    auto id = FaceHalProperties::enrollment_hit().value_or(0);
    auto enrolls = FaceHalProperties::enrollments();
    auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
    if (id < 0 || !isEnrolled) {
        LOG(ERROR) << (isEnrolled ? "invalid enrollment hit" : "Fail: not enrolled");
        if (i < N) {
            auto ac = convertAcquiredInfo(acquiredInfos[i]);
            AuthenticationFrame frame;
            frame.data.acquiredInfo = ac.first;
            frame.data.vendorCode = ac.second;
            cb->onAuthenticationFrame(frame);
            LOG(INFO) << "AcquiredInfo:" << i << ": (" << (int)ac.first << "," << (int)ac.second
                      << ")";
            i++;
        }

        SLEEP_MS(duration / N);
    } while (!Util::hasElapsed(now, duration));

    if (id > 0 && isEnrolled) {
        cb->onAuthenticationSucceeded(id, {} /* hat */);
        return;
    } else {
        LOG(ERROR) << "Fail: face not enrolled";
        cb->onAuthenticationFailed();
        cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
        cb->onError(Error::TIMEOUT, 0 /* vendorError*/);
        return;
    }
}

    cb->onAuthenticationSucceeded(id, {} /* hat */);
std::pair<AcquiredInfo, int32_t> FakeFaceEngine::convertAcquiredInfo(int32_t code) {
    std::pair<AcquiredInfo, int32_t> res;
    if (code > FACE_ACQUIRED_VENDOR_BASE) {
        res.first = AcquiredInfo::VENDOR;
        res.second = code - FACE_ACQUIRED_VENDOR_BASE;
    } else {
        res.first = (AcquiredInfo)code;
        res.second = 0;
    }
    return res;
}

std::pair<Error, int32_t> FakeFaceEngine::convertError(int32_t code) {
    std::pair<Error, int32_t> res;
    if (code > FACE_ERROR_VENDOR_BASE) {
        res.first = Error::VENDOR;
        res.second = code - FACE_ERROR_VENDOR_BASE;
    } else {
        res.first = (Error)code;
        res.second = 0;
    }
    return res;
}

void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel) {
+6 −0
Original line number Diff line number Diff line
@@ -62,6 +62,12 @@ class FakeFaceEngine {
    void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/);

    std::mt19937 mRandom;

  private:
    static constexpr int32_t FACE_ACQUIRED_VENDOR_BASE = 1000;
    static constexpr int32_t FACE_ERROR_VENDOR_BASE = 1000;
    std::pair<AcquiredInfo, int32_t> convertAcquiredInfo(int32_t code);
    std::pair<Error, int32_t> convertError(int32_t code);
};

}  // namespace aidl::android::hardware::biometrics::face
+2 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ On pixel devicse (non-CF), by default (after manufacture reset), Face VHAL is no
Face VHAL enabling is gated by the following two AND conditions:
1. The Face VHAL feature flag (as part of Trunk-development strategy) must be tured until the flags life-cycle ends.
2. The Face VHAL must be enabled via sysprop
See adb commands below

##Getting Stared

@@ -47,8 +48,7 @@ authentication failure, set the hit id to a different value.
$ adb shell setprop vendor.face.virtual.operation_authenticate_duration 800
$ adb shell setprop vendor.face.virtual.enrollment_hit 1
```
Note: At the initial phase of the Face VHAL development, Face-on-camera simulation is not supported, hence
the authentication immediately occurrs  as soon as the authentication request arriving at VHAL.
Refer to face.sysprop for full supported features of authentication, such as error and acquiredInfo insertion


## Enrollment via Setup
+19 −0
Original line number Diff line number Diff line
@@ -157,3 +157,22 @@ prop {
    access: ReadWrite
    api_name: "operation_authenticate_duration"
}

# insert error for authenticate operations
prop {
    prop_name: "vendor.face.virtual.operation_authenticate_error"
    type: Integer
    scope: Internal
    access: ReadWrite
    api_name: "operation_authenticate_error"
}

# acquired info during authentication in format of sequence
prop {
    prop_name: "vendor.face.virtual.operation_authenticate_acquired"
    type: String
    scope: Internal
    access: ReadWrite
    api_name: "operation_authenticate_acquired"
}
Loading