/* * Copyright (C) 2024 The LineageOS Project * * SPDX-License-Identifier: Apache-2.0 */ #include "CancellationSignal.h" #include "Legacy2Aidl.h" #include "Session.h" #include "VendorConstants.h" #include #include #include #include using namespace ::android::fingerprint::samsung; namespace aidl { namespace android { namespace hardware { namespace biometrics { namespace fingerprint { void onClientDeath(void* cookie) { LOG(INFO) << "FingerprintService has died"; Session* session = static_cast(cookie); if (session && !session->isClosed()) { session->close(); } } Session::Session(LegacyHAL hal, int userId, std::shared_ptr cb, LockoutTracker lockoutTracker) : mHal(hal), mLockoutTracker(lockoutTracker), mUserId(userId), mCb(cb) { mDeathRecipient = AIBinder_DeathRecipient_new(onClientDeath); char filename[64]; snprintf(filename, sizeof(filename), "/data/vendor_de/%d/fpdata/"); mHal.ss_fingerprint_set_active_group(userId, &filename[0]); } ndk::ScopedAStatus Session::generateChallenge() { LOG(INFO) << "generateChallenge"; uint64_t challenge = mHal.ss_fingerprint_pre_enroll(); mCb->onChallengeGenerated(challenge); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::revokeChallenge(int64_t challenge) { LOG(INFO) << "revokeChallenge"; mHal.ss_fingerprint_post_enroll(); mCb->onChallengeRevoked(challenge); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::enroll(const HardwareAuthToken& hat, std::shared_ptr* out) { LOG(INFO) << "enroll"; if (FingerprintHalProperties::force_calibrate().value_or(false)) { mHal.request(SEM_REQUEST_FORCE_CBGE, 1); } hw_auth_token_t authToken; translate(hat, authToken); int32_t error = mHal.ss_fingerprint_enroll(&authToken, mUserId, 0 /* timeoutSec */); if (error) { LOG(ERROR) << "ss_fingerprint_enroll failed: " << error; mCb->onError(Error::UNABLE_TO_PROCESS, error); } *out = SharedRefBase::make(this); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::authenticate(int64_t operationId, std::shared_ptr* out) { LOG(INFO) << "authenticate"; int32_t error = mHal.ss_fingerprint_authenticate(operationId, mUserId); if (error) { LOG(ERROR) << "ss_fingerprint_authenticate failed: " << error; mCb->onError(Error::UNABLE_TO_PROCESS, error); } *out = SharedRefBase::make(this); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::detectInteraction(std::shared_ptr* out) { LOG(INFO) << "detectInteraction"; LOG(DEBUG) << "Detect interaction is not supported"; mCb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */); *out = SharedRefBase::make(this); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::enumerateEnrollments() { LOG(INFO) << "enumerateEnrollments"; int32_t error = mHal.ss_fingerprint_enumerate(); if (error) LOG(ERROR) << "ss_fingerprint_enumerate failed: " << error; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::removeEnrollments(const std::vector& enrollmentIds) { LOG(INFO) << "removeEnrollments, size: " << enrollmentIds.size(); for (int32_t enrollment : enrollmentIds) { int32_t error = mHal.ss_fingerprint_remove(mUserId, enrollment); if (error) LOG(ERROR) << "ss_fingerprint_remove failed: " << error; } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::getAuthenticatorId() { LOG(INFO) << "getAuthenticatorId"; mCb->onAuthenticatorIdRetrieved(mHal.ss_fingerprint_get_auth_id()); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::invalidateAuthenticatorId() { LOG(INFO) << "invalidateAuthenticatorId"; mCb->onAuthenticatorIdInvalidated(mHal.ss_fingerprint_get_auth_id()); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::resetLockout(const HardwareAuthToken& /*hat*/) { LOG(INFO) << "resetLockout"; clearLockout(true); mIsLockoutTimerAborted = true; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::close() { LOG(INFO) << "close"; mClosed = true; mCb->onSessionClosed(); AIBinder_DeathRecipient_delete(mDeathRecipient); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::onPointerDown(int32_t /*pointerId*/, int32_t /*x*/, int32_t /*y*/, float /*minor*/, float /*major*/) { LOG(INFO) << "onPointerDown"; if (FingerprintHalProperties::request_touch_event().value_or(false)) { mHal.request(SEM_REQUEST_TOUCH_EVENT, 2); } checkSensorLockout(); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::onPointerUp(int32_t /*pointerId*/) { LOG(INFO) << "onPointerUp"; if (FingerprintHalProperties::request_touch_event().value_or(false)) { mHal.request(SEM_REQUEST_TOUCH_EVENT, 1); } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::onUiReady() { LOG(INFO) << "onUiReady"; // TODO: stub return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::authenticateWithContext( int64_t operationId, const OperationContext& /*context*/, std::shared_ptr* out) { return authenticate(operationId, out); } ndk::ScopedAStatus Session::enrollWithContext(const HardwareAuthToken& hat, const OperationContext& /*context*/, std::shared_ptr* out) { return enroll(hat, out); } ndk::ScopedAStatus Session::detectInteractionWithContext(const OperationContext& /*context*/, std::shared_ptr* out) { return detectInteraction(out); } ndk::ScopedAStatus Session::onPointerDownWithContext(const PointerContext& context) { return onPointerDown(context.pointerId, context.x, context.y, context.minor, context.major); } ndk::ScopedAStatus Session::onPointerUpWithContext(const PointerContext& context) { return onPointerUp(context.pointerId); } ndk::ScopedAStatus Session::onContextChanged(const OperationContext& /*context*/) { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::onPointerCancelWithContext(const PointerContext& /*context*/) { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::setIgnoreDisplayTouches(bool /*shouldIgnore*/) { return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Session::cancel() { int32_t ret = mHal.ss_fingerprint_cancel(); if (ret == 0) { mCb->onError(Error::CANCELED, 0 /* vendorCode */); return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromServiceSpecificError(ret); } } binder_status_t Session::linkToDeath(AIBinder* binder) { return AIBinder_linkToDeath(binder, mDeathRecipient, this); } bool Session::isClosed() { return mClosed; } // Translate from errors returned by traditional HAL (see fingerprint.h) to // AIDL-compliant Error Error Session::VendorErrorFilter(int32_t error, int32_t* vendorCode) { *vendorCode = 0; switch (error) { case FINGERPRINT_ERROR_HW_UNAVAILABLE: return Error::HW_UNAVAILABLE; case FINGERPRINT_ERROR_UNABLE_TO_PROCESS: return Error::UNABLE_TO_PROCESS; case FINGERPRINT_ERROR_TIMEOUT: return Error::TIMEOUT; case FINGERPRINT_ERROR_NO_SPACE: return Error::NO_SPACE; case FINGERPRINT_ERROR_CANCELED: return Error::CANCELED; case FINGERPRINT_ERROR_UNABLE_TO_REMOVE: return Error::UNABLE_TO_REMOVE; case FINGERPRINT_ERROR_LOCKOUT: { *vendorCode = FINGERPRINT_ERROR_LOCKOUT; return Error::VENDOR; } default: if (error >= FINGERPRINT_ERROR_VENDOR_BASE) { // vendor specific code. *vendorCode = error - FINGERPRINT_ERROR_VENDOR_BASE; return Error::VENDOR; } } LOG(ERROR) << "Unknown error from fingerprint vendor library: " << error; return Error::UNABLE_TO_PROCESS; } // Translate acquired messages returned by traditional HAL (see fingerprint.h) // to AIDL-compliant AcquiredInfo AcquiredInfo Session::VendorAcquiredFilter(int32_t info, int32_t* vendorCode) { *vendorCode = 0; switch (info) { case FINGERPRINT_ACQUIRED_GOOD: return AcquiredInfo::GOOD; case FINGERPRINT_ACQUIRED_PARTIAL: return AcquiredInfo::PARTIAL; case FINGERPRINT_ACQUIRED_INSUFFICIENT: return AcquiredInfo::INSUFFICIENT; case FINGERPRINT_ACQUIRED_IMAGER_DIRTY: return AcquiredInfo::SENSOR_DIRTY; case FINGERPRINT_ACQUIRED_TOO_SLOW: return AcquiredInfo::TOO_SLOW; case FINGERPRINT_ACQUIRED_TOO_FAST: return AcquiredInfo::TOO_FAST; default: if (info >= FINGERPRINT_ACQUIRED_VENDOR_BASE) { // vendor specific code. *vendorCode = info - FINGERPRINT_ACQUIRED_VENDOR_BASE; return AcquiredInfo::VENDOR; } } LOG(ERROR) << "Unknown acquiredmsg from fingerprint vendor library: " << info; return AcquiredInfo::INSUFFICIENT; } bool Session::checkSensorLockout() { LockoutMode lockoutMode = mLockoutTracker.getMode(); if (lockoutMode == LockoutMode::PERMANENT) { LOG(ERROR) << "Fail: lockout permanent"; mCb->onLockoutPermanent(); mIsLockoutTimerAborted = true; return true; } else if (lockoutMode == LockoutMode::TIMED) { int64_t timeLeft = mLockoutTracker.getLockoutTimeLeft(); LOG(ERROR) << "Fail: lockout timed " << timeLeft; mCb->onLockoutTimed(timeLeft); if (!mIsLockoutTimerStarted) startLockoutTimer(timeLeft); return true; } return false; } void Session::clearLockout(bool clearAttemptCounter) { mLockoutTracker.reset(clearAttemptCounter); mCb->onLockoutCleared(); } void Session::startLockoutTimer(int64_t timeout) { mIsLockoutTimerAborted = false; std::function action = std::bind(&Session::lockoutTimerExpired, this); std::thread([timeout, action]() { std::this_thread::sleep_for(std::chrono::milliseconds(timeout)); action(); }).detach(); mIsLockoutTimerStarted = true; } void Session::lockoutTimerExpired() { if (!mIsLockoutTimerAborted) clearLockout(false); mIsLockoutTimerStarted = false; mIsLockoutTimerAborted = false; } void Session::notify(const fingerprint_msg_t* msg) { switch (msg->type) { case FINGERPRINT_ERROR: { int32_t vendorCode = 0; Error result = VendorErrorFilter(msg->data.error, &vendorCode); LOG(DEBUG) << "onError(" << static_cast(result) << ")"; mCb->onError(result, vendorCode); } break; case FINGERPRINT_ACQUIRED: { int32_t vendorCode = 0; AcquiredInfo result = VendorAcquiredFilter(msg->data.acquired.acquired_info, &vendorCode); LOG(DEBUG) << "onAcquired(" << static_cast(result) << ")"; mCb->onAcquired(result, vendorCode); } break; case FINGERPRINT_TEMPLATE_ENROLLING: if (FingerprintHalProperties::uses_percentage_samples().value_or(false)) { const_cast(msg)->data.enroll.samples_remaining = 100 - msg->data.enroll.samples_remaining; } if (FingerprintHalProperties::cancel_on_enroll_completion().value_or(false)) { if (msg->data.enroll.samples_remaining == 0) mHal.ss_fingerprint_cancel(); } LOG(DEBUG) << "onEnrollResult(fid=" << msg->data.enroll.finger.fid << ", gid=" << msg->data.enroll.finger.gid << ", rem=" << msg->data.enroll.samples_remaining << ")"; mCb->onEnrollmentProgress(msg->data.enroll.finger.fid, msg->data.enroll.samples_remaining); break; case FINGERPRINT_TEMPLATE_REMOVED: { LOG(DEBUG) << "onRemove(fid=" << msg->data.removed.finger.fid << ", gid=" << msg->data.removed.finger.gid << ", rem=" << msg->data.removed.remaining_templates << ")"; std::vector enrollments; enrollments.push_back(msg->data.removed.finger.fid); mCb->onEnrollmentsRemoved(enrollments); } break; case FINGERPRINT_AUTHENTICATED: { LOG(DEBUG) << "onAuthenticated(fid=" << msg->data.authenticated.finger.fid << ", gid=" << msg->data.authenticated.finger.gid << ")"; if (msg->data.authenticated.finger.fid != 0) { const hw_auth_token_t hat = msg->data.authenticated.hat; HardwareAuthToken authToken; translate(hat, authToken); mCb->onAuthenticationSucceeded(msg->data.authenticated.finger.fid, authToken); mLockoutTracker.reset(true); } else { mCb->onAuthenticationFailed(); mLockoutTracker.addFailedAttempt(); checkSensorLockout(); } } break; case FINGERPRINT_TEMPLATE_ENUMERATING: { LOG(DEBUG) << "onEnumerate(fid=" << msg->data.enumerated.finger.fid << ", gid=" << msg->data.enumerated.finger.gid << ", rem=" << msg->data.enumerated.remaining_templates << ")"; static std::vector enrollments; enrollments.push_back(msg->data.enumerated.finger.fid); if (msg->data.enumerated.remaining_templates == 0) { mCb->onEnrollmentsEnumerated(enrollments); enrollments.clear(); } } break; } } } // namespace fingerprint } // namespace biometrics } // namespace hardware } // namespace android } // namespace aidl