docs: define LAN ABSDK and local experience architecture
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
# LAN ABSDK and Local Experience Design
|
||||
|
||||
## Status
|
||||
|
||||
Approved on 2026-08-24 by the project owner. This specification replaces the three-provider model in `feat/provider-login`.
|
||||
|
||||
## Goal
|
||||
|
||||
ABox Manager exposes exactly two choices before authentication:
|
||||
|
||||
1. **ABox** uses the real vendor ABSDK bridge to communicate with the smart-home gateway available on the current LAN.
|
||||
2. **Local experience** uses production-safe simulation implementations, establishes a simulated session immediately, and enters the authenticated app without requesting credentials.
|
||||
|
||||
The app no longer offers or plans a self-hosted ABSDK-compatible backend.
|
||||
|
||||
## Confirmed Environment and Constraints
|
||||
|
||||
- The observed gateway address is `192.168.100.1`.
|
||||
- The gateway responds to the ABSDK-compatible route `POST /abox/index.php/abjhsdk/user/validate`.
|
||||
- `libABSDK0630.jar` hardcodes `a-box.com.cn/abox/index.php/` and does not expose a supported base-URL setter.
|
||||
- `setHostInfo(url, hotelName, roomNo, apikey)` submits `url` as an initialization form field and does not change the HTTP target.
|
||||
- Hardware testing on `feat/moler-infrared-control` confirms that the current network environment allows the vendor ABSDK to reach the real gateway.
|
||||
- Debug and release builds expose the same provider choices and routing behavior. Release is not treated as a special case.
|
||||
- The vendor JAR uses cleartext HTTP. The existing `@CleartextAbsdkTransport` opt-in remains mandatory, and the Android application must explicitly permit the required LAN transport.
|
||||
- Fake and real sessions, devices, state, and credentials must never be mixed.
|
||||
|
||||
## Provider Model
|
||||
|
||||
`ProviderType` contains only:
|
||||
|
||||
```kotlin
|
||||
enum class ProviderType {
|
||||
ABOX,
|
||||
LOCAL_EXPERIENCE,
|
||||
}
|
||||
```
|
||||
|
||||
The built-in profiles are:
|
||||
|
||||
```text
|
||||
abox-default -> ProviderType.ABOX
|
||||
local-experience -> ProviderType.LOCAL_EXPERIENCE
|
||||
```
|
||||
|
||||
The persisted profile store remains tolerant of removed enum values. A stored `COMPATIBLE_BACKEND` profile is ignored. If it was active, the app returns to provider selection rather than attempting migration to another provider.
|
||||
|
||||
## User Flow
|
||||
|
||||
### ABox
|
||||
|
||||
```text
|
||||
Provider selection
|
||||
-> choose ABox
|
||||
-> save and activate ABox profile
|
||||
-> restore automatic-login preferences when available
|
||||
-> otherwise show username/password login
|
||||
-> SessionCoordinator delegates to the process-wide AbsdkBridge
|
||||
-> enter authenticated app only after DomainResult.Success<Session>
|
||||
```
|
||||
|
||||
Invalid credentials, network errors, and token failures remain on the login screen and display localized errors.
|
||||
|
||||
### Local experience
|
||||
|
||||
```text
|
||||
Provider selection
|
||||
-> choose Local experience
|
||||
-> save and activate local profile
|
||||
-> SimulationSessionGateway establishes Username("local") session
|
||||
-> enter authenticated app immediately
|
||||
```
|
||||
|
||||
Local experience:
|
||||
|
||||
- never displays the credential form;
|
||||
- never calls ABSDK;
|
||||
- never reads or writes Keystore credentials;
|
||||
- uses production-safe simulation device gateways;
|
||||
- can later be removed as a single provider/module boundary if the team decides not to ship it.
|
||||
|
||||
## Module Boundaries
|
||||
|
||||
### `:absdk-bridge`
|
||||
|
||||
Remains the anti-corruption layer around the vendor JAR and implements:
|
||||
|
||||
- `SessionGateway`;
|
||||
- `SocketGateway`;
|
||||
- `TemperatureHumidityGateway`;
|
||||
- `InfraredGateway`.
|
||||
|
||||
A single process-wide `AbsdkBridge` instance is shared by session and device operations so the mutex and vendor singleton Token remain aligned.
|
||||
|
||||
### New `:runtime:simulation`
|
||||
|
||||
A production Kotlin/JVM module implementing:
|
||||
|
||||
- `LocalSessionGateway` as `SimulationSessionGateway`;
|
||||
- `DeviceCatalog` as `SimulationDeviceCatalog`;
|
||||
- `SocketGateway` as `SimulationSocketGateway`;
|
||||
- `TemperatureHumidityGateway` as `SimulationTemperatureHumidityGateway`;
|
||||
- `InfraredGateway` as `SimulationInfraredGateway`.
|
||||
|
||||
These implementations provide deterministic local experience behavior but do not expose test-only failure injection, mutable call-recording APIs, or assertion helpers.
|
||||
|
||||
### `:testing:fakes`
|
||||
|
||||
Remains test-only. It keeps scriptable errors, call recording, and other testing controls. It must not be added as an application runtime dependency.
|
||||
|
||||
### `:android:core:session`
|
||||
|
||||
Keeps provider/profile persistence, Keystore credential storage, and `LocalSessionGatewayImpl` only until the new simulation gateway replaces it. After migration, the old local gateway implementation is removed to prevent two production local-session implementations.
|
||||
|
||||
### `:android:app`
|
||||
|
||||
Owns the Hilt composition root and provider-aware routing. It injects:
|
||||
|
||||
```text
|
||||
SessionGateway -> AbsdkBridge
|
||||
LocalSessionGateway -> SimulationSessionGateway
|
||||
```
|
||||
|
||||
For device operations, the app exposes provider-routing adapters:
|
||||
|
||||
```text
|
||||
RoutingDeviceCatalog
|
||||
RoutingSocketGateway
|
||||
RoutingTemperatureHumidityGateway
|
||||
RoutingInfraredGateway
|
||||
```
|
||||
|
||||
Each adapter observes or reads the active `ProviderProfile` and delegates:
|
||||
|
||||
```text
|
||||
ABOX -> real AbsdkBridge implementation
|
||||
LOCAL_EXPERIENCE -> simulation implementation
|
||||
```
|
||||
|
||||
No feature UI selects Real or Fake directly.
|
||||
|
||||
## Session and Credential Rules
|
||||
|
||||
- `SessionCoordinator` continues to select the session gateway from `ProviderType`.
|
||||
- ABox credentials can use remember-username and automatic-login settings.
|
||||
- Automatic-login passwords are stored through Android Keystore.
|
||||
- Local experience cannot persist credentials or login preferences requiring a password.
|
||||
- Switching provider logs out the current gateway before activating another profile.
|
||||
- Logging out of ABox clears credentials and the Bridge-local session but leaves the ABox profile active so the login page can be shown again.
|
||||
- Logging out of local experience clears the local session and active profile, returning to provider selection.
|
||||
- Only a session whose `profileId` matches the active profile can produce the authenticated UI state.
|
||||
|
||||
## Provider Availability
|
||||
|
||||
`ProviderAvailability` is simplified to the two supported options. Both are enabled in all build types:
|
||||
|
||||
```kotlin
|
||||
data class ProviderAvailability(
|
||||
val abox: Availability = Availability.Enabled,
|
||||
val local: Availability = Availability.Enabled,
|
||||
)
|
||||
```
|
||||
|
||||
`Availability.Simulation` and compatible-backend-specific disabled reasons are removed unless another current UI requirement uses them.
|
||||
|
||||
## Android Transport
|
||||
|
||||
The application declares:
|
||||
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
```
|
||||
|
||||
and permits the vendor cleartext transport required by the current ABSDK. This is an explicit acceptance of the current LAN integration constraint, not a claim that the transport is encrypted.
|
||||
|
||||
No password, Token, API key, raw vendor response, or infrared payload may be logged.
|
||||
|
||||
## UI and Copy
|
||||
|
||||
Provider selection presents exactly:
|
||||
|
||||
- **ABox** — connects to the ABox smart-home gateway through ABSDK on the current LAN;
|
||||
- **Local experience** — uses simulated local devices without network access.
|
||||
|
||||
All compatibility-backend copy, status labels, and branches are removed from English and Simplified Chinese resources.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- ABox authentication failure maps to the localized invalid-credentials message and does not create a session.
|
||||
- Network failure maps to the localized network message and does not create a session.
|
||||
- Token invalidation clears the Bridge-local session and returns the app to login.
|
||||
- Selecting local experience fails visibly if profile persistence or simulated-session creation fails.
|
||||
- Routing adapters return `DomainError.InvalidRequest` when no active profile exists or the profile type is unsupported.
|
||||
- Cancellation is rethrown rather than converted to a normal failure.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit tests
|
||||
|
||||
- `ProviderType` and provider selection expose exactly ABox and local experience.
|
||||
- Removed persisted compatible profiles are ignored and cannot become active.
|
||||
- `SessionCoordinator` routes ABox to `SessionGateway` and local experience to `LocalSessionGateway`.
|
||||
- Arbitrary ABox credentials do not succeed unless the injected real/fake test gateway returns success.
|
||||
- Local experience selection creates a session without calling the ABox gateway or credential store.
|
||||
- Each routing adapter delegates ABox calls to the real test double and local calls to simulation.
|
||||
- Switching providers logs out the previous session and isolates state.
|
||||
|
||||
### Integration and build verification
|
||||
|
||||
- Debug and release compile with the same real ABSDK and simulation modules.
|
||||
- Hilt resolves one binding for each public gateway.
|
||||
- Android lint validates Internet/cleartext configuration.
|
||||
- Full command: `./gradlew check testDebugUnitTest test`.
|
||||
|
||||
### Authorized hardware validation
|
||||
|
||||
On a device connected to the gateway LAN:
|
||||
|
||||
1. Choose ABox.
|
||||
2. Enter a known-invalid credential pair and verify the app stays on login.
|
||||
3. Enter an authorized valid credential pair and verify authenticated navigation.
|
||||
4. Validate Token using the shared Bridge.
|
||||
5. Perform one read-only device query before any control operation.
|
||||
6. Choose local experience and verify no ABSDK call or network dependency is required.
|
||||
|
||||
Sensitive credentials and Tokens are never captured in test reports.
|
||||
|
||||
## Documentation Changes
|
||||
|
||||
The implementation updates:
|
||||
|
||||
- `docs/requirement_spec.md`;
|
||||
- `docs/project_plan.md`;
|
||||
- `docs/architecture.md`;
|
||||
- `docs/protocol/absdk-0630.md`.
|
||||
|
||||
The documents must no longer state that the product connects to an ABox cloud service or a self-hosted compatible backend. They must distinguish confirmed LAN behavior from the JAR's still-hardcoded host behavior.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Reimplementing the ABSDK HTTP/signature protocol;
|
||||
- patching or rewriting the vendor JAR;
|
||||
- adding a self-hosted compatibility server;
|
||||
- adding an in-app gateway IP field that the current JAR cannot consume;
|
||||
- merging the authenticated app shell PR;
|
||||
- deciding whether local experience remains in the final product beyond preserving it as a removable module boundary.
|
||||
Reference in New Issue
Block a user