#include "FakeFaceEngine.h" #include #include #include "util/CancellationSignal.h" #include "util/Util.h" using namespace ::android::face::virt; namespace aidl::android::hardware::biometrics::face { FaceSensorType FakeFaceEngine::GetSensorType() { std::string type = FaceHalProperties::type().value_or(""); if (type == "IR") { return FaceSensorType::IR; } else { FaceHalProperties::type("RGB"); return FaceSensorType::RGB; } } common::SensorStrength FakeFaceEngine::GetSensorStrength() { std::string strength = FaceHalProperties::strength().value_or(""); if (strength == "convenience") { return common::SensorStrength::CONVENIENCE; } else if (strength == "weak") { return common::SensorStrength::WEAK; } else { FaceHalProperties::strength("strong"); return common::SensorStrength::STRONG; } } void FakeFaceEngine::generateChallengeImpl(ISessionCallback* cb) { BEGIN_OP(0); std::uniform_int_distribution dist; auto challenge = dist(mRandom); FaceHalProperties::challenge(challenge); cb->onChallengeGenerated(challenge); } void FakeFaceEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) { BEGIN_OP(0); FaceHalProperties::challenge({}); cb->onChallengeRevoked(challenge); } void FakeFaceEngine::getEnrollmentConfigImpl(ISessionCallback* /*cb*/, std::vector* /*return_val*/) {} void FakeFaceEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat, EnrollmentType /*enrollmentType*/, const std::vector& /*features*/, const std::future& cancel) { BEGIN_OP(FaceHalProperties::operation_start_enroll_latency().value_or(0)); // format is ",::,::... auto nextEnroll = FaceHalProperties::next_enrollment().value_or(""); // Erase the next enrollment FaceHalProperties::next_enrollment({}); AuthenticationFrame frame; frame.data.acquiredInfo = AcquiredInfo::START; frame.data.vendorCode = 0; cb->onAuthenticationFrame(frame); // Do proper HAT verification in the real implementation. if (hat.mac.empty()) { LOG(ERROR) << "Fail: hat"; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return; } if (FaceHalProperties::operation_enroll_fails().value_or(false)) { LOG(ERROR) << "Fail: operation_enroll_fails"; cb->onError(Error::VENDOR, 0 /* vendorError */); return; } auto parts = Util::split(nextEnroll, ","); if (parts.size() < 2) { LOG(ERROR) << "Fail: invalid next_enrollment for : " << nextEnroll; cb->onError(Error::VENDOR, 0 /* vendorError */); return; } auto enrollmentId = std::stoi(parts[0]); const int numBuckets = parts.size() - 1; for (size_t i = 1; i < parts.size(); i++) { auto enrollHit = Util::split(parts[i], ":"); if (enrollHit.size() != 3) { LOG(ERROR) << "Error when unpacking enrollment hit: " << parts[i]; cb->onError(Error::VENDOR, 0 /* vendorError */); } std::string bucket = enrollHit[0]; std::string delay = enrollHit[1]; std::string succeeds = enrollHit[2]; SLEEP_MS(std::stoi(delay)); if (shouldCancel(cancel)) { LOG(ERROR) << "Fail: cancel"; cb->onError(Error::CANCELED, 0 /* vendorCode */); return; } if (!IS_TRUE(succeeds)) { // end and failed LOG(ERROR) << "Fail: requested by caller: " << parts[i]; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */); return; } EnrollmentFrame frame; frame.data.acquiredInfo = AcquiredInfo::GOOD; frame.data.vendorCode = 0; cb->onEnrollmentFrame(frame); frame.data.acquiredInfo = AcquiredInfo::VENDOR; frame.data.vendorCode = std::stoi(bucket); cb->onEnrollmentFrame(frame); int remainingBuckets = numBuckets - i; if (remainingBuckets > 0) { cb->onEnrollmentProgress(enrollmentId, remainingBuckets); } } auto enrollments = FaceHalProperties::enrollments(); enrollments.push_back(enrollmentId); FaceHalProperties::enrollments(enrollments); LOG(INFO) << "enrolled : " << enrollmentId; cb->onEnrollmentProgress(enrollmentId, 0); } void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationId*/, const std::future& 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 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)); } if (FaceHalProperties::operation_authenticate_fails().value_or(false)) { LOG(ERROR) << "Fail: operation_authenticate_fails"; cb->onError(Error::VENDOR, 0 /* vendorError */); return; } if (FaceHalProperties::lockout().value_or(false)) { LOG(ERROR) << "Fail: lockout"; cb->onLockoutPermanent(); cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */); return; } 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"); cb->onAuthenticationFailed(); cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return; } cb->onAuthenticationSucceeded(id, {} /* hat */); } void FakeFaceEngine::detectInteractionImpl(ISessionCallback* cb, const std::future& cancel) { BEGIN_OP(FaceHalProperties::operation_detect_interaction_latency().value_or(0)); if (FaceHalProperties::operation_detect_interaction_fails().value_or(false)) { LOG(ERROR) << "Fail: operation_detect_interaction_fails"; cb->onError(Error::VENDOR, 0 /* vendorError */); return; } 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) << "Fail: not enrolled"; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return; } cb->onInteractionDetected(); } void FakeFaceEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) { BEGIN_OP(0); std::vector enrollments; for (const auto& enrollmentId : FaceHalProperties::enrollments()) { if (enrollmentId) { enrollments.push_back(*enrollmentId); } } cb->onEnrollmentsEnumerated(enrollments); } void FakeFaceEngine::removeEnrollmentsImpl(ISessionCallback* cb, const std::vector& enrollmentIds) { BEGIN_OP(0); std::vector> newEnrollments; for (const auto& enrollment : FaceHalProperties::enrollments()) { auto id = enrollment.value_or(0); if (std::find(enrollmentIds.begin(), enrollmentIds.end(), id) == enrollmentIds.end()) { newEnrollments.emplace_back(id); } } FaceHalProperties::enrollments(newEnrollments); cb->onEnrollmentsRemoved(enrollmentIds); } void FakeFaceEngine::getFeaturesImpl(ISessionCallback* cb) { BEGIN_OP(0); if (FaceHalProperties::enrollments().empty()) { cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */); return; } std::vector featuresToReturn = {}; for (const auto& feature : FaceHalProperties::features()) { if (feature) { featuresToReturn.push_back((Feature)(*feature)); } } cb->onFeaturesRetrieved(featuresToReturn); } void FakeFaceEngine::setFeatureImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat, Feature feature, bool enabled) { BEGIN_OP(0); if (FaceHalProperties::enrollments().empty()) { LOG(ERROR) << "Unable to set feature, enrollments are empty"; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */); return; } if (hat.mac.empty()) { LOG(ERROR) << "Unable to set feature, invalid hat"; cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */); return; } auto features = FaceHalProperties::features(); auto itr = std::find_if(features.begin(), features.end(), [feature](const auto& theFeature) { return *theFeature == (int)feature; }); if (!enabled && (itr != features.end())) { features.erase(itr); } else if (enabled && (itr == features.end())) { features.push_back((int)feature); } FaceHalProperties::features(features); cb->onFeatureSet(feature); } void FakeFaceEngine::getAuthenticatorIdImpl(ISessionCallback* cb) { BEGIN_OP(0); // If this is a weak HAL return 0 per the spec. if (GetSensorStrength() != common::SensorStrength::STRONG) { cb->onAuthenticatorIdRetrieved(0); } else { cb->onAuthenticatorIdRetrieved(FaceHalProperties::authenticator_id().value_or(0)); } } void FakeFaceEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) { BEGIN_OP(0); int64_t authenticatorId = FaceHalProperties::authenticator_id().value_or(0); int64_t newId = authenticatorId + 1; FaceHalProperties::authenticator_id(newId); cb->onAuthenticatorIdInvalidated(newId); } void FakeFaceEngine::resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/) { BEGIN_OP(0); FaceHalProperties::lockout(false); cb->onLockoutCleared(); } } // namespace aidl::android::hardware::biometrics::face