docs: map existing codebase

This commit is contained in:
2026-03-30 15:21:07 +08:00
commit 3c2bb02e33
7 changed files with 1320 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
# Architecture
**Analysis Date:** 2026-03-30
## Pattern Overview
**Overall:** Flutter Plugin Architecture with Platform Interface Pattern
**Key Characteristics:**
- Flutter plugin wrapping ArcSoft Face Recognition SDK (Android only)
- Platform interface abstraction for cross-platform extensibility
- Method channel communication between Dart and native Android
- Singleton pattern for native FaceEngine management
- Comprehensive error code enumeration for SDK error handling
## Layers
**Dart Public API Layer:**
- Purpose: Provides user-facing API for face recognition operations
- Location: `lib/arc.dart`
- Contains: Public methods with detailed documentation
- Depends on: `ArcPlatform` interface
- Used by: Flutter applications consuming this plugin
**Platform Interface Layer:**
- Purpose: Abstract interface for platform-specific implementations
- Location: `lib/arc_platform_interface.dart`
- Contains: Abstract method definitions, platform instance management
- Depends on: `plugin_platform_interface` package
- Used by: `Arc` class, mock implementations for testing
**Method Channel Layer:**
- Purpose: Implements platform interface using Flutter method channels
- Location: `lib/arc_method_channel.dart`
- Contains: Method channel invocation logic, parameter marshalling
- Depends on: Flutter `MethodChannel`, `ArcPlatform` interface
- Used by: Default platform instance registration
**Android Native Plugin Layer:**
- Purpose: Handles method channel calls, dispatches to engine manager
- Location: `android/src/main/java/com/xiarui/arc/ArcPlugin.java`
- Contains: Method call handler, parameter validation, result marshalling
- Depends on: `FaceEngineManager`, Flutter plugin APIs
- Used by: Flutter engine via plugin registration
**Android Engine Manager Layer:**
- Purpose: Manages ArcSoft FaceEngine singleton, executes SDK operations
- Location: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`
- Contains: SDK activation, engine lifecycle, face detection/extraction/comparison logic
- Depends on: ArcSoft SDK (`arcsoft_face.jar`)
- Used by: `ArcPlugin` handler methods
**Data Model Layer:**
- Purpose: Encapsulates face information and error codes
- Location: `android/src/main/java/com/xiarui/arc/FaceInfo.java`, `FaceErrorCode.java`
- Contains: Data structures for face results, comprehensive error code enumeration
- Depends on: Android SDK (`Rect` class)
- Used by: All native layers for data transfer
## Data Flow
**SDK Activation Flow:**
1. Flutter app calls `Arc.activeOnline(appId, sdkKey, activeKey)`
2. `Arc` delegates to `ArcPlatform.instance.activeOnline()`
3. `MethodChannelArc` invokes method channel with `'activeOnline'`
4. `ArcPlugin.handleActiveOnline()` validates parameters
5. `FaceEngineManager.getInstance().activeOnline()` calls ArcSoft SDK
6. Error code mapped to `FaceErrorCode` enum
7. Result Map returned through method channel to Flutter
**Face Detection Flow:**
1. Flutter app calls `Arc.detectFaces(data, width, height, format)`
2. Method channel invokes `'detectFaces'` with byte array payload
3. `ArcPlugin.handleDetectFaces()` validates image parameters
4. `FaceEngineManager.detectFaces()` executes SDK face detection
5. RGB liveness detection processed via `faceEngine.process()` and `getLiveness()`
6. `FaceInfo` list converted to Map structure via `convertFaceInfoToList()`
7. Result includes faceList, rgbLiveness, isRgbAlive
**Face Feature Extraction Flow:**
1. Flutter app calls `Arc.extractFaceFeature()` with face rect from detection
2. Native layer creates `FaceInfo` object from rect coordinates
3. `FaceEngineManager.extractFaceFeature()` calls SDK extract method
4. Feature data (byte array) returned in result Map
**Feature Comparison Flow:**
1. Flutter app calls `Arc.compareFaceFeature(featureData1, featureData2, compareModel)`
2. Native layer creates `FaceFeature` objects from byte arrays
3. `FaceEngineManager.compareFaceFeature()` executes SDK comparison
4. Similarity score (0.0-1.0) returned in result Map
**State Management:**
- Singleton `FaceEngineManager` holds `FaceEngine` instance
- Engine initialization state tracked via `isInitialized` flag
- Last RGB liveness result cached in `lastRgbLiveness` field
## Key Abstractions
**ArcPlatform Interface:**
- Purpose: Platform-agnostic interface for face recognition operations
- Examples: `lib/arc_platform_interface.dart`
- Pattern: Platform Interface pattern with token verification
**FaceEngineManager Singleton:**
- Purpose: Centralized management of ArcSoft FaceEngine lifecycle
- Examples: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`
- Pattern: Singleton with synchronized getInstance()
**FaceErrorCode Enumeration:**
- Purpose: Maps SDK numeric error codes to human-readable messages
- Examples: `android/src/main/java/com/xiarui/arc/FaceErrorCode.java`
- Pattern: Enum with code lookup via `fromCode()`
**FaceInfo Data Model:**
- Purpose: Represents single face detection result
- Examples: `android/src/main/java/com/xiarui/arc/FaceInfo.java`
- Pattern: JavaBean with `toMap()` for Flutter serialization
## Entry Points
**Flutter Plugin Entry:**
- Location: `lib/arc.dart`
- Triggers: Flutter app imports and instantiates `Arc` class
- Responsibilities: Public API facade, delegates to platform interface
**Android Plugin Registration:**
- Location: `android/src/main/java/com/xiarui/arc/ArcPlugin.java`
- Triggers: Flutter engine attaches plugin via `onAttachedToEngine()`
- Responsibilities: Method channel setup, call dispatching
**Example App Entry:**
- Location: `example/lib/main.dart`
- Triggers: App launch via `void main()`
- Responsibilities: Demonstrates SDK activation, engine init, camera-based face detection
## Error Handling
**Strategy:** Consistent Map return structure with success/error fields
**Patterns:**
- All public methods return `Map<String, dynamic>?` with keys: `success`, `errorCode`, `message`
- Native layer uses `FaceErrorCode.fromCode()` for error mapping
- Custom error codes for internal failures (e.g., `ENGINE_NOT_INITIALIZED = -1`)
- Flutter layer receives structured error info, no exception throwing for SDK errors
- Platform exceptions caught in example app with `PlatformException` handler
## Cross-Cutting Concerns
**Logging:** Android native uses `android.util.Log` with tag "FaceEngineManager"
**Validation:** Native methods validate engine initialization, image dimensions (width %4, height %2 for NV21)
**Authentication:** SDK requires online activation with appId/sdkKey/activeKey from ArcSoft console
**Image Format:** NV21 (format code 2050) is primary supported format for camera streams
---
*Architecture analysis: 2026-03-30*

View File

@@ -0,0 +1,195 @@
# Codebase Concerns
**Analysis Date:** 2026-03-30
## Tech Debt
**iOS Platform Support Missing:**
- Issue: The plugin only supports Android platform; iOS implementation is completely absent
- Files: `pubspec.yaml` (no iOS platform definition), `lib/arc_platform_interface.dart`, `lib/arc_method_channel.dart`
- Impact: Plugin cannot be used in iOS applications; limits market reach
- Fix approach: Create iOS native implementation using Swift/Objective-C with ArcSoft SDK iOS bindings
**Unimplemented Mock Methods in Tests:**
- Issue: `MockArcPlatform` in test file does not implement `registerFaceFeature` and `registerFaceFeatureBatch` methods
- Files: `test/arc_test.dart` (lines 8-60)
- Impact: Tests will fail when calling registration methods; incomplete test coverage
- Fix approach: Add mock implementations for all missing methods in `MockArcPlatform`
**Placeholder Documentation:**
- Issue: CHANGELOG.md and LICENSE files contain placeholder TODO text
- Files: `CHANGELOG.md` (line 3), `LICENSE` (line 1)
- Impact: Project appears incomplete; may confuse users about licensing
- Fix approach: Replace placeholder text with actual content
## Known Bugs
**No explicit engine disposal:**
- Symptoms: `FaceEngine` instance in `FaceEngineManager` is never nullified after `unInit()`
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 268-278)
- Trigger: Calling `unInit()` then attempting to reuse engine
- Workaround: Re-initialize engine after disposal
**State inconsistency after initialization failure:**
- Symptoms: If `init()` fails, `isInitialized` remains false but `faceEngine` object still exists
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 141-159)
- Trigger: Engine initialization with invalid parameters
- Workaround: Check both `isInitialized` flag and error code before proceeding
## Security Considerations
**Hardcoded SDK Credentials in Example:**
- Risk: Real SDK credentials (appId, sdkKey, activeKey) hardcoded in example application
- Files: `example/lib/main.dart` (lines 41-43)
```dart
final String _appId = '4nPFuS2TYAQh9werHL2qbKjtTH9nnoixk7G6yqSUyjVH';
final String _sdkKey = 'aWMTT3coxNQFETg9f3BGHCiBznAApXmcHFF3J5yQbsZ';
final String _activeKey = 'NEAT7AU4KLCEK682';
```
- Current mitigation: Comment instructs users to replace with own keys (line 286)
- Recommendations: Use environment variables or config file; never commit real credentials; add `.env.example` template
**Credential Transmission via Method Channel:**
- Risk: Sensitive credentials (appId, sdkKey, activeKey) passed through method channel without encryption
- Files: `lib/arc_method_channel.dart` (lines 24-28), `android/src/main/java/com/xiarui/arc/ArcPlugin.java` (lines 95-97)
- Current mitigation: None
- Recommendations: Credentials are passed to native SDK which handles secure activation; acceptable for current architecture
**Feature Data Privacy:**
- Risk: Face feature data transmitted between Flutter and native layers could be intercepted
- Files: `lib/arc_method_channel.dart`, `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`
- Current mitigation: Data stays within app boundary
- Recommendations: Ensure no logging of feature data; consider encryption for stored features
## Performance Bottlenecks
**Large Binary Data Transfer:**
- Problem: Image data (NV21 format, potentially several MB per frame) transferred through method channel for each detection
- Files: `lib/arc_method_channel.dart` (lines 55-61), `android/src/main/java/com/xiarui/arc/ArcPlugin.java` (lines 154-157)
- Cause: Method channel serialization overhead for large byte arrays
- Improvement path: Consider using FFI for direct memory access; reduce image resolution; implement frame skipping
**Real-time Detection Frame Rate:**
- Problem: `_isDetecting` flag prevents concurrent processing but may cause frame drops
- Files: `example/lib/main.dart` (lines 406-407, 471-473)
- Cause: Sequential processing - new frames wait until current detection completes
- Improvement path: Implement queue-based processing with configurable frame skip; use dedicated detection thread
**Feature Extraction Overhead:**
- Problem: Feature extraction called synchronously, blocking UI thread potential
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 365-366)
- Cause: SDK operation runs on method channel thread
- Improvement path: Move heavy processing to background thread pool; provide async callbacks
## Fragile Areas
**NV21 Format Constraints:**
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 179-189)
- Why fragile: Image width must be multiple of 4, height must be multiple of 2 for NV21 format - violation causes runtime errors
- Safe modification: Always validate dimensions before calling SDK; add Flutter-side validation
- Test coverage: No tests for invalid dimension handling
**Singleton State Management:**
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (lines 54-59)
- Why fragile: Singleton pattern with mutable `lastRgbLiveness` field; concurrent access risk
- Safe modification: Add synchronization for mutable fields; or use ThreadLocal for liveness results
- Test coverage: No concurrent access tests
**Camera Preview Orientation:**
- Files: `example/lib/main.dart` (line 556)
- Why fragile: Fixed `quarterTurns: -1` rotation may not work for all devices/orientations
- Safe modification: Calculate rotation based on device orientation and camera sensor orientation
- Test coverage: No tests for different device orientations
## Scaling Limits
**Face Database Size:**
- Current capacity: Depends on ArcSoft SDK limits (typically 50,000 faces)
- Limit: Memory consumption increases with registered faces; search time increases
- Scaling path: Implement pagination; use external database for large-scale deployments; consider face clustering
**Image Resolution:**
- Current capacity: Default camera resolution (medium preset)
- Limit: Higher resolution = slower detection, more memory usage
- Scaling path: Allow configurable resolution; implement dynamic resolution adjustment based on device capability
**Concurrent Detection Requests:**
- Current capacity: Single detection at a time (`_isDetecting` flag)
- Limit: Multiple simultaneous requests queue up
- Scaling path: Implement request queue with priority; batch processing; dedicated worker threads
## Dependencies at Risk
**ArcSoft Face SDK:**
- Risk: Third-party proprietary SDK; licensing and availability concerns
- Impact: Plugin entirely dependent on ArcSoft SDK availability and compatibility
- Migration plan: Abstract SDK interface; allow alternative face recognition backends (e.g., ML Kit, OpenCV)
**camera package:**
- Risk: External dependency in example app; version compatibility
- Impact: Example app functionality dependent on camera plugin stability
- Migration plan: Keep camera package updated; abstract camera interface for alternative implementations
**Flutter SDK Version:**
- Risk: Requires SDK ^3.9.0 which is relatively new
- Files: `pubspec.yaml` (line 7)
- Impact: Older Flutter versions cannot use plugin
- Migration plan: Consider lowering SDK constraint if backward compatibility needed
## Missing Critical Features
**Face Search (1:N Matching):**
- Problem: `registerFaceFeature` and `registerFaceFeatureBatch` exist but no `searchFaceFeature` method to query registered faces
- Files: `lib/arc.dart`, `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`
- Blocks: Cannot implement 1:N face recognition workflow (find matching face from database)
**Engine Uninit from Flutter:**
- Problem: No Flutter API to release engine resources
- Files: `lib/arc.dart` (no unInit method)
- Blocks: Memory leak if engine not properly released; cannot reset engine state
**Error Code Documentation for Flutter:**
- Problem: Error codes defined in Java but no Flutter-accessible documentation
- Files: `android/src/main/java/com/xiarui/arc/FaceErrorCode.java` (comprehensive), `lib/arc.dart` (no error code constants)
- Blocks: Flutter developers cannot interpret error codes without referencing native code
**IR Liveness Detection:**
- Problem: Only RGB liveness detection implemented; IR liveness not exposed
- Files: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` (only ASF_LIVENESS mask)
- Blocks: Cannot use more secure IR-based liveness detection if hardware available
## Test Coverage Gaps
**Native Unit Tests:**
- What's not tested: All methods except `getPlatformVersion` in `ArcPluginTest.java`
- Files: `android/src/test/java/com/xiarui/arc/ArcPluginTest.java` (only 1 test method)
- Risk: Native method handling bugs undetected
- Priority: High
**Flutter Integration Tests:**
- What's not tested: Actual face detection with real image data; end-to-end workflows
- Files: No integration test files in `test/` directory
- Risk: Real-world usage patterns not validated
- Priority: High
**Method Channel Tests:**
- What's not tested: All methods except `getPlatformVersion` in method channel test
- Files: `test/arc_method_channel_test.dart` (lines 24-26)
- Risk: Method channel communication bugs for complex methods undetected
- Priority: Medium
**Edge Case Tests:**
- What's not tested: Invalid parameters, null data, oversized images, engine not initialized
- Files: All test files lack edge case coverage
- Risk: Runtime crashes on invalid inputs
- Priority: Medium
**Mock Implementation Completeness:**
- What's not tested: `registerFaceFeature`, `registerFaceFeatureBatch` methods in mock
- Files: `test/arc_test.dart` (`MockArcPlatform` missing methods)
- Risk: Tests fail or skip when new methods are used
- Priority: Low
---
*Concerns audit: 2026-03-30*

View File

@@ -0,0 +1,239 @@
# Coding Conventions
**Analysis Date:** 2026-03-30
## Naming Patterns
**Files:**
- Dart: lowercase with underscores (`arc.dart`, `arc_platform_interface.dart`, `arc_method_channel.dart`)
- Java: PascalCase matching class name (`ArcPlugin.java`, `FaceEngineManager.java`)
**Classes:**
- PascalCase: `Arc`, `ArcPlatform`, `MethodChannelArc`, `FaceEngineManager`, `FaceErrorCode`
**Methods/Functions:**
- camelCase: `getPlatformVersion`, `activeOnline`, `detectFaces`, `extractFaceFeature`
**Variables:**
- Private Dart members: underscore prefix (`_token`, `_instance`, `_isDetecting`)
- Java: `private` keyword with camelCase: `faceEngine`, `isInitialized`, `lastRgbLiveness`
**Constants:**
- Dart: `static final` with underscore for private: `static final Object _token = Object();`
- Java enum: uppercase with underscores: `MOK`, `MERR_UNKNOWN`, `ENGINE_NOT_INITIALIZED`
## Code Style
**Formatting:**
- Dart: `flutter_lints ^5.0.0` via `analysis_options.yaml`
- Config: `include: package:flutter_lints/flutter.yaml`
- Java: Standard Android/Java conventions
**Linting:**
- Dart: flutter_lints package rules
- Java: No explicit linting config detected
## Import Organization
**Dart Order:**
1. SDK imports (`dart:typed_data`)
2. Package imports (`package:plugin_platform_interface/plugin_platform_interface.dart`)
3. Relative imports (`arc_method_channel.dart`)
**Example:**
```dart
import 'dart:typed_data';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'arc_method_channel.dart';
```
**Java Order:**
1. Package declaration
2. Android SDK imports
3. Third-party imports
4. JUnit/Mockito imports (in tests)
## Error Handling
**Dart Patterns:**
- Platform interface throws `UnimplementedError` for abstract methods:
```dart
Future<String?> getPlatformVersion() {
throw UnimplementedError('platformVersion() has not been implemented.');
}
```
- Method channel calls wrapped in try-catch with `PlatformException`
- Return `Map<String, dynamic>?` with `success`, `errorCode`, `message` keys
**Java Patterns:**
- Parameter validation before processing
- Error codes via `FaceErrorCode` enum
- Result Map with consistent keys: `success`, `errorCode`, `message`
- Logging with `android.util.Log.e()` for errors
**Return Format:**
All API methods return consistent Map structure:
```dart
{
'success': bool, // Operation success status
'errorCode': int, // Error code (0 = success)
'message': String, // Error/success message
// Additional data keys based on method
}
```
## Documentation
**Dart Comments:**
- Triple-slash `///` for method documentation
- Chinese descriptions required
- Parameter documentation: `[paramName] description`
- Return documentation after parameters
**Example:**
```dart
/// 激活 SDK在线激活
/// [appId] 应用 ID从虹软控制台获取
/// [sdkKey] SDK 密钥(从虹软控制台获取)
/// [activeKey] 激活密钥
/// 返回包含 success, errorCode, message 的 Map
Future<Map<String, dynamic>?> activeOnline({
required String appId,
required String sdkKey,
required String activeKey,
})
```
**Java Comments:**
- Javadoc `/** */` format
- `@param` and `@return` tags
- Chinese descriptions
**Example:**
```java
/**
* 激活 SDK在线激活方式
* @param context Android 上下文
* @param appId 应用 ID从虹软控制台获取
* @param sdkKey SDK 密钥(从虹软控制台获取)
* @param activeKey 激活密钥
* @return 错误码0 表示成功)
*/
public int activeOnline(Context context, String appId, String sdkKey, String activeKey)
```
## Logging
**Dart:**
- `debugPrint()` for development logging
- Located in: `example/lib/main.dart`
**Java:**
- `android.util.Log.i()` for info
- `android.util.Log.e()` for errors
- `android.util.Log.w()` for warnings
- Tag: `"FaceEngineManager"`
**Example:**
```java
android.util.Log.i("FaceEngineManager", "引擎初始化成功!");
android.util.Log.e("FaceEngineManager", "引擎初始化失败,错误码: " + result);
```
## Function Design
**Parameters:**
- Dart: Named parameters with `required` keyword
- Optional parameters have default values
**Example:**
```dart
Future<Map<String, dynamic>?> detectFaces({
required Uint8List data,
required int width,
required int height,
int format = 2050, // Optional with default
})
```
**Return Values:**
- All async methods return `Future<Map<String, dynamic>?>`
- Null-safe with nullable type annotation `?`
## Module Design
**Platform Interface Pattern:**
- Abstract class extends `PlatformInterface`
- Static instance getter/setter
- Token verification for platform registration
**Example Structure:**
```dart
abstract class ArcPlatform extends PlatformInterface {
ArcPlatform() : super(token: _token);
static final Object _token = Object();
static ArcPlatform _instance = MethodChannelArc();
static ArcPlatform get instance => _instance;
static set instance(ArcPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
}
```
**Method Channel Implementation:**
- Class extends platform interface
- `@visibleForTesting` annotation for test exposure
- Method channel with constant name: `'arc'`
**Singleton Pattern (Java):**
```java
public class FaceEngineManager {
private static FaceEngineManager instance;
private FaceEngineManager() {}
public static synchronized FaceEngineManager getInstance() {
if (instance == null) {
instance = new FaceEngineManager();
}
return instance;
}
}
```
## State Management
**Flutter Widget State:**
- Private state variables with underscore prefix
- `StatefulWidget` with corresponding `State` class
- `mounted` check before `setState`
**Example:**
```dart
class _HomePageState extends State<HomePage> {
String _platformVersion = 'Unknown';
bool _isActivated = false;
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
```
## Key Files
**Core Dart Files:**
- `lib/arc.dart` - Main API class
- `lib/arc_platform_interface.dart` - Abstract platform interface
- `lib/arc_method_channel.dart` - Method channel implementation
**Core Java Files:**
- `android/src/main/java/com/xiarui/arc/ArcPlugin.java` - Flutter plugin entry
- `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` - Engine singleton
- `android/src/main/java/com/xiarui/arc/FaceErrorCode.java` - Error code enum
---
*Convention analysis: 2026-03-30*

View File

@@ -0,0 +1,158 @@
# External Integrations
**Analysis Date:** 2026-03-30
## APIs & External Services
**人脸识别 SDK:**
- 虹软 (ArcSoft) 人脸识别 SDK - 离线人脸检测、特征提取、活体检测
- SDK 库: `arcsoft_face.jar`, `libarcsoft_face.so`
- 版本: 基于 2024年1月版本
- Auth: 需要 AppId、SdkKey、ActiveKey从虹软开放平台获取
- 激活方式: 在线激活(`FaceEngine.activeOnline()`
- 官方文档: https://ai.arcsoft.com.cn
**图像处理工具:**
- 虹软图像处理工具库 - 图像格式转换和处理
- SDK 库: `arcsoft_image_util.jar`, `libarcsoft_image_util.so`
## Data Storage
**Databases:**
- 无数据库集成 - 人脸特征数据存储在内存中(人脸库)
**Face Feature Database (In-Memory):**
- 人脸库存储: 通过 `FaceEngine.registerFaceFeature()` 注册到 SDK 内部内存
- 数据结构: `FaceFeatureInfo(searchId, featureData, faceTag)`
- 搜索能力: 1:N 人脸搜索SDK 内置)
- 注意: 人脸库数据在引擎释放后丢失,不持久化
**File Storage:**
- 虹软 SDK 激活文件存储于设备本地SDK 自动管理)
- 位置: Android 设备内部存储SDK 控制)
**Caching:**
- None - 无缓存层
## Authentication & Identity
**Auth Provider:**
- 虹软 SDK 激活机制 - 设备绑定激活
- Implementation: 在线激活验证 AppId + SdkKey + ActiveKey
- 激活文件: 设备指纹绑定,单设备单激活
- 错误处理: `FaceErrorCode` 枚举定义完整错误码映射
**激活状态检查:**
```dart
// 激活成功条件: errorCode == 0 或 errorCode == 90114 (已激活)
result['success'] = errorCode == 0 || errorCode == 90114;
```
**激活流程:**
1. 调用 `Arc.activeOnline(appId, sdkKey, activeKey)`
2. Flutter MethodChannel 传递参数到 Android
3. Android 调用 `FaceEngine.activeOnline(context, activeKey, appId, sdkKey)`
4. SDK 与虹软服务器验证并生成激活文件
5. 返回激活结果 Map
## Monitoring & Observability
**Error Tracking:**
- 错误码枚举系统 - `FaceErrorCode.java`(完整错误码映射)
- 分类: 通用错误、SDK基础错误、人脸识别错误、激活错误、网络错误、离线授权错误
**Logs:**
- Android Logcat 输出(`FaceEngineManager.java` 使用 `android.util.Log`
- Flutter debugPrint 输出(示例应用)
- 日志级别: Info, Error
**关键日志点:**
- SDK 初始化结果: `FaceEngineManager.init()`
- 人脸检测结果: `FaceEngineManager.detectFaces()`
- RGB 活体检测结果: `FaceEngineManager.getLiveness()`
- 特征提取结果: `FaceEngineManager.extractFaceFeature()`
- 人脸比对结果: `FaceEngineManager.compareFaceFeature()`
## CI/CD & Deployment
**Hosting:**
- 本地 Flutter 插件包 - 不发布到 pub.dev私有包
- `publish_to: 'none'`(示例应用)
**CI Pipeline:**
- None - 无 CI 配置文件
**Build Commands:**
```bash
flutter pub get # 获取依赖
flutter test # 运行 Flutter 单元测试
flutter build apk # 构建 Android APK
cd android && ./gradlew test # 运行 Android 单元测试
```
## Environment Configuration
**Required env vars (SDK 激活):**
- AppId: 虹软开放平台申请的应用 ID
- SdkKey: 虹软开放平台申请的 SDK 密钥
- ActiveKey: 激活密钥(可选,部分场景需要)
**示例应用配置(硬编码):**
```dart
// example/lib/main.dart
final String _appId = '4nPFuS2TYAQh9werHL2qbKjtTH9nnoixk7G6yqSUyjVH';
final String _sdkKey = 'aWMTT3coxNQFETg9f3BGHCiBznAApXmcHFF3J5yQbsZ';
final String _activeKey = 'NEAT7AU4KLCEK682';
```
**Secrets location:**
- 当前: 硬编码在示例应用(不推荐生产使用)
- 建议: 通过环境变量或安全存储传递
## Webhooks & Callbacks
**Incoming:**
- None - 无外部 webhook
**Outgoing:**
- None - 无外部 API 调用SDK 激活除外)
## Flutter-Native Communication
**Method Channel:**
- Channel 名称: `'arc'`
- 定义文件: `lib/arc_method_channel.dart`
- 实现: `MethodChannelArc`
**支持的方法:**
| 方法名 | 功能 | 参数 |
|--------|------|------|
| `getPlatformVersion` | 获取平台版本 | 无 |
| `activeOnline` | SDK 在线激活 | appId, sdkKey, activeKey |
| `init` | 初始化人脸引擎 | detectMode, orient, maxFaceNum, combinedMask |
| `detectFaces` | 人脸检测+RGB活体 | data, width, height, format |
| `extractFaceFeature` | 特征提取 | data, width, height, rect, faceId, extractType, mask |
| `compareFaceFeature` | 特征比对 | featureData1, featureData2, compareModel |
| `registerFaceFeature` | 单张人脸注册 | searchId, featureData, faceTag |
| `registerFaceFeatureBatch` | 批量人脸注册 | faceList |
**数据格式:**
- 图像数据: NV21 格式(`Uint8List`
- 特征数据: SDK 定义长度(`Uint8List`
- 返回值: `Map<String, dynamic>` 格式统一
## Image Format Requirements
**NV21 格式约束:**
- 图像宽度必须是 4 的倍数
- 图像高度必须是 2 的倍数NV21 格式)
- 格式码: 2050 (`FaceEngine.CP_PAF_NV21`)
**Camera Integration (Example):**
- 使用 `camera` 插件获取摄像头图像流
- `ImageFormatGroup.nv21` 设置图像格式
- `CameraPreviewScreen` 处理实时图像流检测
---
*Integration audit: 2026-03-30*

109
.planning/codebase/STACK.md Normal file
View File

@@ -0,0 +1,109 @@
# Technology Stack
**Analysis Date:** 2026-03-30
## Languages
**Primary:**
- Dart 3.9.0+ - Flutter 插件层代码(`lib/*.dart`
- Java 11 - Android 原生实现(`android/src/main/java/**/*.java`
**Secondary:**
- Kotlin - 未使用(纯 Java 实现)
- C/C++ (Native) - 虹软 SDK native 库(`libarcsoft_face.so`, `libarcsoft_face_engine.so`, `libarcsoft_image_util.so`
## Runtime
**Environment:**
- Flutter SDK 3.18.0+ (stable channel)
- Dart SDK 3.9.0+ (<4.0.0)
- Android SDK compileSdk 36, minSdk 24
**Package Manager:**
- pub (Flutter/Dart 包管理器)
- Gradle 8.9.1 (Android 构建)
- Lockfile: `pubspec.lock` (present), `pubspec.yaml` (present)
## Frameworks
**Core:**
- Flutter SDK - 跨平台移动应用框架
- Flutter Plugin Architecture - 平台通道通信机制
**Testing:**
- flutter_test - Flutter 单元测试框架
- flutter_lints 5.0.0 - Dart/Flutter 代码规范检查
- JUnit 4.13.2 - Java 单元测试
- Mockito 5.0.0 - Java mock 测试框架
**Build/Dev:**
- Gradle 8.9.1 - Android 构建系统
- Android Gradle Plugin 8.9.1 - Android 构建插件
- Flutter CLI - Flutter 命令行工具
## Key Dependencies
**Critical:**
- `plugin_platform_interface` 2.0.2 - Flutter 插件平台接口抽象层
- `arcsoft_face.jar` - 虹软人脸识别核心 SDK
- `arcsoft_image_util.jar` - 虹软图像处理工具库
- `libarcsoft_face.so` (~87MB) - 虹软人脸识别 native 库arm64-v8a
- `libarcsoft_face_engine.so` (~1.3MB) - 虹软人脸引擎 native 库
- `libarcsoft_image_util.so` (~80KB) - 虹软图像处理 native 库
**Infrastructure (Example App):**
- `camera` 0.11.1 - 摄像头访问插件(用于实时人脸检测)
- `cupertino_icons` 1.0.8 - iOS 风格图标
## Configuration
**Environment:**
- Dart SDK 版本约束: `^3.9.0``pubspec.yaml`
- Flutter SDK 版本约束: `>=3.3.0``pubspec.yaml`
- Java 版本: 11`android/build.gradle`
- Pub 仓库源: `https://pub.flutter-io.cn`(中国镜像)
**Build:**
- `pubspec.yaml` - Dart/Flutter 包配置
- `pubspec.lock` - 依赖锁定文件
- `analysis_options.yaml` - Dart 代码分析配置(继承 flutter_lints
- `android/build.gradle` - Android 构建配置
- `android/settings.gradle` - Android 项目设置
## Platform Requirements
**Development:**
- Flutter SDK 3.18.0+ 安装
- Dart SDK 3.9.0+ 安装
- Android Studio / Android SDKcompileSdk 36
- JDK 11
**Production:**
- Android 设备API 24+,即 Android 7.0+
- ARM64-v8a 或 armeabi-v7a 架构支持
- 摄像头权限(用于人脸检测)
- 虹软 SDK 有效激活AppId、SdkKey、ActiveKey
## Architecture Support
**Native Libraries:**
- `arm64-v8a` - 64位 ARM 设备(主流现代设备)
- `armeabi-v7a` - 32位 ARM 设备(兼容旧设备)
**SDK 功能掩码常量:**
```java
ASF_FACE_DETECT = 0x00000001 // 人脸检测
ASF_FACE_RECOGNITION = 0x00000004 // 人脸特征
ASF_AGE = 0x00000008 // 年龄检测
ASF_GENDER = 0x00000010 // 性别检测
ASF_LIVENESS = 0x00000080 // RGB 活体检测
ASF_IMAGEQUALITY = 0x00000200 // 图像质量检测
ASF_MASK_DETECT = 0x00001000 // 口罩检测
ASF_UPDATE_FACEDATA = 0x00002000 // 人脸信息更新
```
**默认功能掩码:** 0x85 = 人脸检测 + 人脸特征 + RGB活体检测
---
*Stack analysis: 2026-03-30*

View File

@@ -0,0 +1,175 @@
# Codebase Structure
**Analysis Date:** 2026-03-30
## Directory Layout
```
D:\code\new_git_code\arc/
├── lib/ # Flutter Dart source code
├── android/ # Android native implementation
├── test/ # Unit tests for Dart code
├── example/ # Example Flutter application
├── docs/ # Documentation files
├── .dart_tool/ # Dart tool cache (generated)
├── .planning/ # Planning documents (generated)
└── pubspec.yaml # Flutter package manifest
```
## Directory Purposes
**lib:**
- Purpose: Flutter plugin Dart source code
- Contains: Public API, platform interface, method channel implementation
- Key files: `arc.dart`, `arc_platform_interface.dart`, `arc_method_channel.dart`
**android:**
- Purpose: Android native implementation for ArcSoft SDK integration
- Contains: Java source, ArcSoft SDK libraries, Gradle build config
- Key files: `src/main/java/com/xiarui/arc/ArcPlugin.java`, `libs/arcsoft_face.jar`
**test:**
- Purpose: Unit tests for Dart plugin code
- Contains: Mock platform implementations, method channel tests
- Key files: `arc_test.dart`, `arc_method_channel_test.dart`
**example:**
- Purpose: Demonstrates plugin usage with camera-based face detection
- Contains: Complete Flutter app with camera preview and detection UI
- Key files: `lib/main.dart`, `integration_test/plugin_integration_test.dart`
**docs:**
- Purpose: External SDK documentation
- Contains: ArcSoft face recognition API documentation
- Key files: `虹软人脸识别接口文档.md`
**android/libs:**
- Purpose: ArcSoft native SDK JAR files
- Contains: `arcsoft_face.jar` (face recognition), `arcsoft_image_util.jar` (image utilities)
- Generated: No (third-party SDK)
- Committed: Yes
**android/src/main/jniLibs:**
- Purpose: Native library files for different CPU architectures
- Contains: `.so` files for arm64-v8a, armeabi-v7a
- Generated: No (from ArcSoft SDK)
- Committed: Yes
## Key File Locations
**Entry Points:**
- `lib/arc.dart`: Public API entry point for plugin consumers
- `android/src/main/java/com/xiarui/arc/ArcPlugin.java`: Android plugin registration and method handling
- `example/lib/main.dart`: Example app entry point
**Configuration:**
- `pubspec.yaml`: Flutter package configuration, SDK version constraints
- `android/build.gradle`: Android library build configuration, SDK dependencies
- `android/src/main/AndroidManifest.xml`: Android permissions (CAMERA, INTERNET, STORAGE)
**Core Logic:**
- `lib/arc_platform_interface.dart`: Platform interface abstract class
- `lib/arc_method_channel.dart`: Method channel implementation
- `android/src/main/java/com/xiarui/arc/FaceEngineManager.java`: Singleton managing FaceEngine lifecycle
**Data Models:**
- `android/src/main/java/com/xiarui/arc/FaceInfo.java`: Face detection result model
- `android/src/main/java/com/xiarui/arc/FaceErrorCode.java`: Error code enumeration (586 lines)
**Testing:**
- `test/arc_test.dart`: Mock platform tests
- `test/arc_method_channel_test.dart`: Method channel unit tests
- `android/src/test/java/com/xiarui/arc/ArcPluginTest.java`: Android unit tests
## Naming Conventions
**Files:**
- Dart: lowercase with underscores, e.g., `arc_platform_interface.dart`
- Java: PascalCase for classes, e.g., `ArcPlugin.java`, `FaceEngineManager.java`
**Classes:**
- Dart: PascalCase, e.g., `Arc`, `ArcPlatform`, `MethodChannelArc`
- Java: PascalCase, e.g., `FaceEngineManager`, `FaceErrorCode`
**Methods:**
- Dart: camelCase with descriptive names, e.g., `activeOnline`, `detectFaces`, `extractFaceFeature`
- Java: camelCase with handler prefix for dispatch, e.g., `handleActiveOnline`, `handleDetectFaces`
**Variables:**
- Dart: camelCase for local/instance variables
- Java: camelCase, enum constants use UPPER_SNAKE_CASE, e.g., `MERR_ASF_ACTIVATION_FAIL`
## Where to Add New Code
**New Feature (Dart API):**
- Primary code: `lib/arc.dart` - add public method
- Interface: `lib/arc_platform_interface.dart` - add abstract method
- Implementation: `lib/arc_method_channel.dart` - add method channel invocation
**New Feature (Android Native):**
- Handler: `android/src/main/java/com/xiarui/arc/ArcPlugin.java` - add case in `onMethodCall()` switch
- Logic: `android/src/main/java/com/xiarui/arc/FaceEngineManager.java` - add SDK operation method
**New Platform Support (iOS/Web):**
- Create platform-specific implementation in appropriate directory
- Register in `pubspec.yaml` under `plugin.platforms`
**New Data Model:**
- Location: `android/src/main/java/com/xiarui/arc/` alongside existing models
- Pattern: JavaBean style with getters/setters and `toMap()` method
**Tests:**
- Dart tests: `test/` directory, named `*_test.dart`
- Android tests: `android/src/test/java/com/xiarui/arc/` directory
**Utilities:**
- Shared helpers: `android/src/main/java/com/xiarui/arc/` as utility classes
## Special Directories
**android/libs:**
- Purpose: ArcSoft SDK JAR files (third-party)
- Generated: No
- Committed: Yes (required for build)
- Note: Contains `arcsoft_face.jar`, `arcsoft_image_util.jar`
**android/src/main/jniLibs:**
- Purpose: Native `.so` libraries for CPU architectures
- Generated: No (from ArcSoft SDK distribution)
- Committed: Yes
- Architectures: arm64-v8a, armeabi-v7a
**example/build:**
- Purpose: Flutter/Gradle build artifacts
- Generated: Yes
- Committed: No (should be in .gitignore)
**.dart_tool:**
- Purpose: Dart package config and tool cache
- Generated: Yes
- Committed: No
**.planning:**
- Purpose: GSD planning documents
- Generated: Yes (by GSD tools)
- Committed: Yes (for team visibility)
## Package Structure
**Flutter Package:**
- Package name: `arc`
- Version: 0.0.1
- SDK constraint: `^3.9.0`
- Flutter constraint: `>=3.3.0`
- Platforms: Android only (current)
**Android Package:**
- Package name: `com.xiarui.arc`
- Plugin class: `ArcPlugin`
- Min SDK: 24 (Android 7.0)
- Compile SDK: 36
- Java version: 11
---
*Structure analysis: 2026-03-30*

View File

@@ -0,0 +1,284 @@
# Testing Patterns
**Analysis Date:** 2026-03-30
## Test Framework
**Runner:**
- Flutter Test Framework (flutter_test SDK)
- Dart SDK: ^3.9.0
- Config: `analysis_options.yaml` (linting only, no test config file)
**Assertion Library:**
- flutter_test package: `expect()`, `findsOneWidget`, `isInstanceOf<T>()`
**Run Commands:**
```bash
flutter test # Run all tests
flutter test test/arc_test.dart # Run specific test file
flutter test --coverage # Run with coverage
```
## Test File Organization
**Location:**
- Plugin tests: `test/` directory (co-located with lib)
- Example tests: `example/test/` directory
- Java tests: `android/src/test/java/`
**Naming:**
- Dart: `*_test.dart` pattern (`arc_test.dart`, `arc_method_channel_test.dart`, `widget_test.dart`)
- Java: `*Test.java` pattern (`ArcPluginTest.java`)
**Structure:**
```
arc/
├── test/
│ ├── arc_test.dart # Platform interface tests
│ └── arc_method_channel_test.dart # Method channel tests
├── example/
│ └── test/
│ └── widget_test.dart # Widget tests
└── android/
└── src/
└── test/
└── java/
└── com/xiarui/arc/
└── ArcPluginTest.java
```
## Test Structure
**Dart Unit Tests:**
```dart
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final ArcPlatform initialPlatform = ArcPlatform.instance;
test('$MethodChannelArc is the default instance', () {
expect(initialPlatform, isInstanceOf<MethodChannelArc>());
});
test('getPlatformVersion', () async {
Arc arcPlugin = Arc();
MockArcPlatform fakePlatform = MockArcPlatform();
ArcPlatform.instance = fakePlatform;
expect(await arcPlugin.getPlatformVersion(), '42');
});
}
```
**Java Unit Tests:**
```java
public class ArcPluginTest {
@Test
public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
ArcPlugin plugin = new ArcPlugin();
final MethodCall call = new MethodCall("getPlatformVersion", null);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
plugin.onMethodCall(call, mockResult);
verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
}
}
```
## Mocking
**Dart Mocking:**
- `MockPlatformInterfaceMixin` from plugin_platform_interface
- Custom mock class implementing platform interface
**Example:**
```dart
class MockArcPlatform
with MockPlatformInterfaceMixin
implements ArcPlatform {
@override
Future<String?> getPlatformVersion() => Future.value('42');
@override
Future<Map<String, dynamic>?> activeOnline({
required String appId,
required String sdkKey,
required String activeKey,
}) => Future.value({'success': true, 'errorCode': 0, 'message': 'success'});
}
```
**Method Channel Mocking:**
```dart
setUp(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
channel,
(MethodCall methodCall) async {
return '42';
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
});
```
**Java Mocking:**
- Mockito framework
- `mock(Class.class)` for mock objects
- `verify(mock).method(args)` for verification
**What to Mock:**
- Platform interfaces for unit testing
- Method channels for integration testing
- Native engine calls
**What NOT to Mock:**
- Data transformation logic (test directly)
- Simple utility functions
## Fixtures and Factories
**Test Data:**
- Mock return values defined inline
- Simple string/int values: `'42'`, `0`, `true`
**Example Mock Data:**
```dart
@override
Future<Map<String, dynamic>?> detectFaces({
required Uint8List data,
required int width,
required int height,
int format = 2050,
}) => Future.value({
'success': true,
'errorCode': 0,
'faceList': [],
'rgbLiveness': 1,
'isRgbAlive': true
});
```
**Location:**
- Mock implementations in test files
- No separate fixture files
## Coverage
**Requirements:** None enforced
**View Coverage:**
```bash
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
```
## Test Types
**Unit Tests:**
- Platform interface instantiation
- Method channel default instance verification
- Mock platform behavior testing
- File: `test/arc_test.dart`, `test/arc_method_channel_test.dart`
**Integration Tests:**
- Method channel handler testing
- Native plugin method call verification
- File: `android/src/test/java/com/xiarui/arc/ArcPluginTest.java`
**Widget Tests:**
- Basic widget rendering verification
- File: `example/test/widget_test.dart`
**E2E Tests:** Not used
## Common Patterns
**Async Testing:**
```dart
test('getPlatformVersion', () async {
expect(await arcPlugin.getPlatformVersion(), '42');
});
```
**Platform Instance Mocking:**
```dart
test('methodName', () async {
Arc arcPlugin = Arc();
MockArcPlatform fakePlatform = MockArcPlatform();
ArcPlatform.instance = fakePlatform; // Override instance
expect(await arcPlugin.method(), expectedResult);
});
```
**Setup/Teardown:**
```dart
setUp(() {
// Initialize test binding, set up mocks
TestWidgetsFlutterBinding.ensureInitialized();
});
tearDown(() {
// Clean up mocks
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
});
```
**Error Testing:**
Current tests do not explicitly test error scenarios. Recommended pattern for future:
```dart
test('throws on invalid params', () async {
expect(
() => arcPlugin.detectFaces(data: Uint8List(0), width: 0, height: 0),
throwsA(isA<PlatformException>()),
);
});
```
## Test Naming Convention
**Dart:**
- Simple descriptive names: `'getPlatformVersion'`
- Interpolated class names: `'$MethodChannelArc is the default instance'`
**Java:**
- Pattern: `methodName_scenario_expectedResult`
- Example: `onMethodCall_getPlatformVersion_returnsExpectedValue`
## Key Test Files
**Plugin Core Tests:**
- `test/arc_test.dart` - Platform interface and Arc class tests
- `test/arc_method_channel_test.dart` - Method channel implementation tests
**Example App Tests:**
- `example/test/widget_test.dart` - Basic widget test (template, needs updating)
**Native Tests:**
- `android/src/test/java/com/xiarui/arc/ArcPluginTest.java` - Android plugin tests
## Test Coverage Gaps
**Untested Methods:**
- `activeOnline()` - No test coverage
- `init()` - No test coverage
- `detectFaces()` - No test coverage
- `extractFaceFeature()` - No test coverage
- `compareFaceFeature()` - No test coverage
- `registerFaceFeature()` - No test coverage
- `registerFaceFeatureBatch()` - No test coverage
**Untested Scenarios:**
- Error handling (invalid parameters)
- Edge cases (null data, empty arrays)
- RGB liveness detection flow
- Face feature extraction and comparison
---
*Testing analysis: 2026-03-30*