Palm Vein Blueprint
USB palm vein scanner integration using SDPVUnifiedAPI — capture, ROI detection, enrollment, 1:N identification, and cache pool management
| Feature | palm-vein |
| Category | Integration |
| Version | 2.0.0 |
| Tags | biometric, vein-pattern, hardware, sdk, usb, android |
| YAML Source | View on GitHub |
| JSON API | palm-vein.json |
Actors
| ID | Name | Type | Description |
|---|---|---|---|
host_application | Host Application | system | Android terminal app that calls the SDK API to perform palm vein operations |
palm_scanner | PVM310 Biometric Scanner | external | USB-connected palm vein scanning hardware (vendor-id 31109, product-id 4097) |
usb_manager | USB Permission Manager | system | Android USB host permission handler (PalmUSBManager) in the Application class |
Fields
| Name | Type | Required | Label | Description |
|---|---|---|---|---|
license_path | text | Yes | License File Path | Validations: required |
chip_type | text | Yes | Target Chip Platform | |
proximity_intensity_max | number | Yes | Proximity Intensity Threshold | Validations: min, max |
firmware_version | text | No | Firmware Version | |
serial_number | text | No | Device Serial Number | |
usb_vendor_id | number | Yes | USB Vendor ID | |
usb_product_id | number | Yes | USB Product ID | |
palm_template | json | No | Palm Vein Template | |
palm_token | text | No | Palm Cache Token | |
palm_roi_image | json | No | Palm ROI Image | |
palm_raw_image | json | No | Palm Raw Image | |
palm_hand | select | No | Enrolled Hand | |
timeout_seconds | number | Yes | Operation Timeout | Validations: min, max |
image_masked | boolean | Yes | Apply Mask to Display Image | |
led_color | select | No | LED Color | |
led_duration_ms | number | No | LED Duration (ms) | |
cache_pool_count | number | No | Palms in Cache Pool |
States
State field: device_state
Values:
| State | Initial | Terminal |
|---|---|---|
uninitialized | Yes | |
cache_pool_ready | ||
service_ready | ||
usb_waiting | ||
device_open | ||
idle | ||
capturing | ||
enrolling | ||
identifying | ||
device_closed | Yes |
Transitions:
| Name | From | To | Actor | Condition |
|---|---|---|---|---|
uninitialized | cache_pool_ready | host_application | ||
cache_pool_ready | service_ready | host_application | ||
service_ready | usb_waiting | usb_manager | ||
usb_waiting | device_open | usb_manager | ||
device_open | idle | host_application | ||
idle | capturing | host_application | ||
capturing | enrolling | host_application | ||
capturing | identifying | host_application | ||
enrolling | idle | palm_scanner | ||
identifying | idle | palm_scanner | ||
capturing | idle | host_application | ||
idle | device_closed | host_application | ||
device_open | device_closed | usb_manager |
Rules
- sdk_singleton:
- api_access: SDPVUnifiedAPI.getInstance() returns the singleton — all operations go through this instance
- thread_safety: captureImage(), detectRoi(), enroll(), identifyFeature() must run on background threads — never on the main thread
- initialization_sequence:
- order: initCachePool → initService → initModel → (USB permission) → initDevice
- cache_pool_first: initCachePool is time-consuming and must complete before any other SDK call
- chip_type: ChipType.RK3568 is required for the PVM310 terminal hardware
- license_copy: License file must exist at /sdcard/SD_TEMPLATE/LICENSE/license.dat — app copies from res/raw/license.dat on first launch
- model_after_service: initModel() must be called after initService() to load the deep learning algorithm
- usb_permission:
- handler: PalmUSBManager created in Application.onCreate() handles USB attach/detach/permission
- auto_request: USB permission is requested automatically when PalmUSBManager detects the device
- callback: onCheckPermission(0) means granted — then call initDevice(context)
- removal: onUSBRemoved() fires when device is unplugged — call terminateDevice()
- device_filter: USB device filter: vendor-id=31109, product-id=4097
- image_capture:
- loop_pattern: DeviceTookImage thread calls captureImage() in a continuous loop
- success: RETURN_DEVICE_SUCCESS means palm detected — check proximity_intensity before using
- no_trigger: RETURN_DEVICE_ERROR_DEVICE_NOT_TRIGGERED means no palm detected — keep looping
- proximity_check: If proximity_intensity > 500, palm is too close — warn user
- camera_dimensions: Raw image is 400×640 pixels
- frame_queue: Captured frames go into a bounded queue for the detect thread
- roi_detection:
- method: detectRoi(rawImage) extracts the palm vein region of interest
- success: RETURN_SERVICE_SUCCESS means valid ROI — imageRoi can be used for enroll/identify
- failure: Non-success means palm not properly positioned — discard frame and retry
- mask_display: maskImage(rawImage) blurs the image for privacy display to the user
- enrollment:
- listener: setEnrollListener(PalmEnroll) must be called before starting enrollment
- flow: captureImage loop → detectRoi → enroll(EnrollPicture(roiImage, rawImage)) — repeat until enrollComplete
- feature_count: SDPVServiceConstant.FEATURE_NUM captures required (typically 5-6 valid frames)
- callbacks:
- complete: enrollComplete(EnrollResult) — template in result.getTmpl()
- fail: enrollFail(UnifiedMsg) — enrollment failed, user should retry
- progress: enrollTimes(count) — number of successful captures so far
- insert_after: After enrollComplete, call insertPalm(template) to add to cache pool
- token_returned: insertPalm returns a token string for cache management
- custom_token: insertPalm(template, userId) allows custom token for user-linked identification
- identification:
- method: identifyFeature(roiImage, callback) compares against the internal cache pool
- callback: Callback receives (code, message, palmToken) — code == RETURN_SERVICE_SUCCESS means match found
- token_resolution: palmToken from callback maps to the token used in insertPalm — resolves to user_id
- led_feedback: Green LED on success (lightLed(LED_GREEN, 1000)), red on failure (lightLed(LED_RED, 1000))
- cache_management:
- insert: insertPalm(template) or insertPalm(template, token) adds to cache pool
- remove: removePalm(token) removes a specific template
- clear: clearCachePool() removes all templates
- count: palmsCount returns the number of templates in the cache
- persistence: Cache pool data stored under /sdcard/SD_TEMPLATE/HANDS_TEMPLATE/
- led_control:
- method: lightLed(LEDColor, durationMs) — LEDColor.LED_RED, LED_GREEN, LED_BLUE
- duration: Duration in milliseconds (e.g., 1000 for 1 second flash)
- palm_positioning:
- distance: Hand must be approximately 15-30cm from the scanner
- proximity_warning: proximity_intensity > 500 means too close — display warning to user
- centered: Hand must be centered on the scanner
- fingers_spread: Fingers should be spread naturally
- security:
- biometric_data_sensitive: Templates, ROI images, and raw images are biometric PII — encrypt at rest
- template_never_transmitted: Templates stay on-device — only tokens transmitted to backend
- license_required: Valid license.dat required for initService()
- storage_paths:
- root: /sdcard/SD_TEMPLATE/
- license: /sdcard/SD_TEMPLATE/LICENSE/license.dat
- templates: /sdcard/SD_TEMPLATE/HANDS_TEMPLATE/
- images: /sdcard/SD_TEMPLATE/PALM_IMG/
Outcomes
Cache_pool_initialized (Priority: 1)
Given:
- App has started
- Cache pool has not been initialized yet
Then:
- call_service target:
SDPVUnifiedAPI.initCachePool— Initialize cache pool with context and ChipType.RK3568 - emit_event event:
palm.cache_pool.initialized
Result: Cache pool initialized — template storage ready
Service_initialized (Priority: 2)
Given:
- Cache pool is initialized
- License file exists at configured path
Then:
- call_service target:
SDPVUnifiedAPI.initService— Initialize service with license file path - call_service target:
SDPVUnifiedAPI.initModel— Load the deep learning model for palm vein recognition - emit_event event:
palm.service.initialized
Result: Algorithm service and model loaded — ready for USB device
Service_init_failed (Priority: 3) — Error: PALM_INVALID_LICENSE
Given:
- License file is missing or invalid
Then:
- emit_event event:
palm.sdk.init_failed
Result: Service initialization fails — check license.dat
Usb_permission_granted (Priority: 4)
Given:
- Service is initialized
- PalmUSBManager detects USB device (vendor 31109, product 4097)
Then:
- call_service target:
PalmUSBManager.initUSBPermission— Request USB permission from Android system - emit_event event:
palm.usb.permission_granted
Result: USB permission granted — ready to open device
Device_opened (Priority: 5)
Given:
- USB permission has been granted
Then:
- call_service target:
SDPVUnifiedAPI.initDevice— Open USB connection to palm scanner - set_field target:
firmware_versionvalue:returned by api.firmwareVersion - set_field target:
serial_numbervalue:returned by api.serialNumber - emit_event event:
palm.device.opened
Result: Device connected — firmware and serial obtained
Device_not_connected (Priority: 6) — Error: PALM_DEVICE_NOT_CONNECTED
Given:
- Service is initialized
- initDevice returns non-success code
Then:
- emit_event event:
palm.device.disconnected
Result: Device not connected — check USB cable and retry
Image_captured (Priority: 7)
Given:
- Device is open and idle
- captureImage returns RETURN_DEVICE_SUCCESS
proximity_intensity_max(system) gte0
Then:
- call_service target:
SDPVUnifiedAPI.detectRoi— Detect palm ROI in the captured image - set_field target:
palm_roi_imagevalue:ROI from detectRoi result - emit_event event:
palm.image.captured
Result: Image captured and ROI detected — ready for enrollment or identification
Palm_too_close (Priority: 8) — Error: PALM_HAND_POSITION_ERROR
Given:
- captureImage returns success
- proximity_intensity exceeds threshold (500)
Then:
- emit_event event:
palm.position.too_close
Result: Palm is too close to scanner — user should move hand back
Template_enrolled (Priority: 9) | Transaction: atomic
Given:
- Device is open and idle
- PalmEnroll listener is set via setEnrollListener()
- captureImage → detectRoi → enroll loop is running
Then:
- call_service target:
SDPVUnifiedAPI.enroll— Feed ROI image + raw image as EnrollPicture into enrollment pipeline - call_service target:
SDPVUnifiedAPI.insertPalm— Insert completed template into cache pool with user token - set_field target:
palm_templatevalue:template from EnrollResult.getTmpl() - set_field target:
palm_tokenvalue:token returned by insertPalm() - create_record target:
palm_templates— Store template in local database linked to user account - emit_event event:
palm.template.registered
Result: Palm enrolled, template in cache pool and database — user can now identify by palm
Enrollment_failed (Priority: 10) — Error: PALM_REGISTRATION_FAILED
Given:
- Enrollment is in progress
- ANY: enrollFail callback fires OR Capture loop times out before enough valid frames OR User moves hand during capture
Then:
- emit_event event:
palm.template.registration_failed
Result: Enrollment failed — user should keep hand steady and try again
Palm_identified (Priority: 11)
Given:
- Device is open and idle
- ROI image available from detectRoi()
- Cache pool has at least one template
Then:
- call_service target:
SDPVUnifiedAPI.identifyFeature— Compare ROI image against cache pool — callback returns (code, msg, palmToken) - call_service target:
SDPVUnifiedAPI.lightLed— Flash green LED on successful identification - emit_event event:
palm.match.succeeded
Result: Palm identified — token resolves to registered user
Identification_failed (Priority: 12) — Error: PALM_VERIFICATION_FAILED
Given:
- identifyFeature callback returns non-success code
Then:
- call_service target:
SDPVUnifiedAPI.lightLed— Flash red LED on failed identification - emit_event event:
palm.match.failed
Result: Palm does not match any registered template
Usb_device_removed (Priority: 13)
Given:
- Device is open
- PalmUSBManager fires onUSBRemoved()
Then:
- call_service target:
SDPVUnifiedAPI.terminateDevice— Clean up device connection - emit_event event:
palm.device.disconnected
Result: USB device removed — scanner unavailable until reconnected
Operation_timed_out (Priority: 14) — Error: PALM_TIMEOUT
Given:
- ANY: Capture loop is running OR Enrollment is in progress OR Identification is in progress
elapsed_time(system) gttimeout_seconds
Then:
- emit_event event:
palm.operation.timeout
Result: Operation timed out — user should try again
Errors
| Code | Status | Message | Retry |
|---|---|---|---|
PALM_INVALID_LICENSE | 403 | Palm vein scanner license is invalid or missing | No |
PALM_DEVICE_NOT_CONNECTED | 500 | Palm vein scanner is not connected — check USB cable | Yes |
PALM_USB_PERMISSION_DENIED | 403 | USB permission denied for palm vein scanner | No |
PALM_DEVICE_BUSY | 409 | Scanner is busy with another operation | Yes |
PALM_EXTRACTION_FAILED | 422 | Could not detect palm vein ROI — reposition your hand | Yes |
PALM_REGISTRATION_FAILED | 422 | Palm enrollment failed — keep your hand steady and try again | Yes |
PALM_VERIFICATION_FAILED | 401 | Palm does not match any registered pattern | No |
PALM_TIMEOUT | 422 | Operation timed out — please try again | Yes |
PALM_HAND_POSITION_ERROR | 422 | Palm is too close — hold your hand 15-30cm from the scanner | Yes |
PALM_COMMUNICATION_FAILED | 500 | USB communication with scanner failed | Yes |
PALM_CACHE_INSERT_FAILED | 500 | Failed to insert template into cache pool | No |
PALM_PARAMETER_ERROR | 400 | Invalid parameter passed to scanner operation | No |
PALM_INSUFFICIENT_MEMORY | 500 | Insufficient memory for scanner operation | No |
Events
| Event | Description | Payload |
|---|---|---|
palm.cache_pool.initialized | Cache pool initialized with template count | cache_pool_count |
palm.service.initialized | Algorithm service and model loaded | |
palm.sdk.init_failed | SDK initialization failed | error_code |
palm.usb.permission_granted | USB permission granted for palm scanner | |
palm.device.opened | Scanner device opened via USB | firmware_version, serial_number |
palm.device.disconnected | Scanner device disconnected or USB removed | error_code |
palm.image.captured | Image captured and ROI detected | proximity_intensity |
palm.position.too_close | Palm is too close to scanner | proximity_intensity |
palm.template.registered | Palm enrolled — template in cache pool and database | user_id, palm_token |
palm.template.registration_failed | Enrollment failed | error_code, stage |
palm.match.succeeded | Palm identified against cache pool | palm_token, user_id |
palm.match.failed | Palm did not match any cached template | error_code |
palm.operation.timeout | Operation exceeded timeout | operation_type, timeout_seconds |
palm.operation.cancelled | Capture/enroll/identify operation cancelled |
Related Blueprints
| Feature | Relationship | Reason |
|---|---|---|
| biometric-auth | recommended | Biometric-auth uses the palm vein SDK to provide alternative authentication |
| palm-pay | optional | Palm pay links palm vein templates to payment proxies for hands-free payment |
| terminal-enrollment | optional | At-terminal enrollment uses palm vein SDK for walk-up registration |
AGI Readiness
Goals
Reliable Palm Vein
USB palm vein scanner integration using SDPVUnifiedAPI — capture, ROI detection, enrollment, 1:N identification, and cache pool management
Success Metrics:
| Metric | Target | Measurement |
|---|---|---|
| success_rate | >= 99.5% | Successful operations divided by total attempts |
| error_recovery_rate | >= 95% | Errors that auto-recover without manual intervention |
Constraints:
- availability (non-negotiable): Must degrade gracefully when USB device is unavailable
- security (non-negotiable): Biometric templates must be encrypted at rest and never logged in plaintext
Autonomy
Level: supervised
Escalation Triggers:
error_rate > 5
Verification
Invariants:
- biometric templates are never logged in plaintext
- all data access is authenticated and authorized
- error messages never expose internal system details
- state transitions follow the defined state machine — no illegal transitions
- USB permission is always checked before device operations
Tradeoffs
| Prefer | Over | Reason |
|---|---|---|
| reliability | throughput | biometric failures erode user trust and block payment |
Safety
| Action | Permission | Cooldown | Max Auto |
|---|---|---|---|
| cache_pool_initialized | autonomous | - | - |
| service_initialized | autonomous | - | - |
| service_init_failed | autonomous | - | - |
| usb_permission_granted | autonomous | - | - |
| device_opened | autonomous | - | - |
| device_not_connected | autonomous | - | - |
| image_captured | autonomous | - | - |
| palm_too_close | autonomous | - | - |
| template_enrolled | autonomous | - | - |
| enrollment_failed | autonomous | - | - |
| palm_identified | autonomous | - | - |
| identification_failed | autonomous | - | - |
| usb_device_removed | autonomous | - | - |
| operation_timed_out | autonomous | - | - |