| /* |
| * 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 <fingerprint.sysprop.h> |
| |
| #include <android-base/logging.h> |
| |
| #include <endian.h> |
| #include <thread> |
| |
| using namespace ::android::fingerprint::samsung; |
| using namespace ::std::chrono_literals; |
| |
| namespace aidl { |
| namespace android { |
| namespace hardware { |
| namespace biometrics { |
| namespace fingerprint { |
| |
| void onClientDeath(void* cookie) { |
| LOG(INFO) << "FingerprintService has died"; |
| Session* session = static_cast<Session*>(cookie); |
| if (session && !session->isClosed()) { |
| session->close(); |
| } |
| } |
| |
| Session::Session(LegacyHAL hal, int userId, std::shared_ptr<ISessionCallback> 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<ICancellationSignal>* out) { |
| LOG(INFO) << "enroll"; |
| |
| if (FingerprintHalProperties::force_calibrate().value_or(false)) { |
| mCaptureReady = 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); |
| } |
| |
| if (FingerprintHalProperties::force_calibrate().value_or(false)) { |
| while (!mCaptureReady) { |
| std::this_thread::sleep_for(100ms); |
| } |
| } |
| |
| *out = SharedRefBase::make<CancellationSignal>(this); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| ndk::ScopedAStatus Session::authenticate(int64_t operationId, |
| std::shared_ptr<ICancellationSignal>* 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<CancellationSignal>(this); |
| return ndk::ScopedAStatus::ok(); |
| } |
| |
| ndk::ScopedAStatus Session::detectInteraction(std::shared_ptr<ICancellationSignal>* out) { |
| LOG(INFO) << "detectInteraction"; |
| |
| LOG(DEBUG) << "Detect interaction is not supported"; |
| mCb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */); |
| |
| *out = SharedRefBase::make<CancellationSignal>(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<int32_t>& 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<ICancellationSignal>* out) { |
| return authenticate(operationId, out); |
| } |
| |
| ndk::ScopedAStatus Session::enrollWithContext(const HardwareAuthToken& hat, |
| const OperationContext& /*context*/, |
| std::shared_ptr<ICancellationSignal>* out) { |
| return enroll(hat, out); |
| } |
| |
| ndk::ScopedAStatus Session::detectInteractionWithContext(const OperationContext& /*context*/, |
| std::shared_ptr<ICancellationSignal>* 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<void()> 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<int>(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<int>(result) << ")"; |
| mCb->onAcquired(result, vendorCode); |
| } break; |
| case FINGERPRINT_TEMPLATE_ENROLLING: |
| if (FingerprintHalProperties::uses_percentage_samples().value_or(false)) { |
| const_cast<fingerprint_msg_t*>(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<int> 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<int> enrollments; |
| enrollments.push_back(msg->data.enumerated.finger.fid); |
| if (msg->data.enumerated.remaining_templates == 0) { |
| mCb->onEnrollmentsEnumerated(enrollments); |
| enrollments.clear(); |
| } |
| } break; |
| } |
| } |
| |
| void Session::onCaptureReady() { |
| mCaptureReady = true; |
| } |
| |
| } // namespace fingerprint |
| } // namespace biometrics |
| } // namespace hardware |
| } // namespace android |
| } // namespace aidl |