15 KiB
Rosetta Android — Architecture
Документ отражает текущее состояние
rosetta-android(веткаdev) по коду на 2026-04-18.
1. Архитектурный профиль
Приложение сейчас устроено как layered + service-oriented архитектура:
- UI:
MainActivity+ Compose-экраны + ViewModel. - DI: Hilt (
@HiltAndroidApp,@AndroidEntryPoint, модули вdi/AppContainer.kt). - Runtime orchestration:
ProtocolRuntime->ProtocolRuntimeCore(+ compatibility facadeProtocolManager),CallManager,TransportManager,UpdateManager. - Session/Identity runtime state:
SessionStore,SessionReducer,IdentityStore. - Data:
MessageRepository,GroupRepository,AccountManager,PreferencesManager. - Persistence: Room (
RosettaDatabase) + DataStore/SharedPreferences.
Основная runtime-логика сети вынесена в instance-класс ProtocolRuntimeCore.
ProtocolManager сохранен как тонкий compatibility facade.
DI-вход в network core идет через ProtocolRuntime (Hilt singleton).
2. Слои и границы
flowchart TB
subgraph ENTRY["Android Entry Points"]
E1["RosettaApplication"]
E2["MainActivity"]
E3["RosettaFirebaseMessagingService"]
E4["IncomingCallActivity / CallForegroundService"]
end
subgraph DI["Hilt Singleton Graph"]
D1["ProtocolGateway"]
D1A["ProtocolRuntime"]
D2["SessionCoordinator"]
D3["IdentityGateway"]
D4["AccountManager / PreferencesManager"]
D5["MessageRepository / GroupRepository"]
D6["UiEntryPoint + EntryPointAccessors"]
end
subgraph SESSION["Session / Identity Runtime"]
S1["SessionStore"]
S2["SessionReducer"]
S3["IdentityStore"]
S4["AppSessionCoordinator"]
end
subgraph NET["Network Runtime"]
N0["ProtocolRuntime"]
N1["ProtocolRuntimeCore"]
N1A["ProtocolManager (compat facade)"]
N2["Protocol"]
N3["PacketSubscriptionRegistry"]
N4["ReadyPacketGate"]
end
subgraph DATA["Data + Persistence"]
R1["MessageRepository"]
R2["GroupRepository"]
R3["Room: RosettaDatabase"]
end
ENTRY --> DI
DI --> SESSION
DI --> NET
DI --> DATA
D1 --> D1A
D1A --> N1
N1A --> N1
SESSION --> NET
DATA --> NET
DATA --> R3
NET --> N2
3. DI и composition root
3.1 Hilt
RosettaApplicationпомечен@HiltAndroidApp.- Entry points уровня Android-компонентов:
MainActivity,IncomingCallActivity,CallForegroundService,RosettaFirebaseMessagingService. - Основные модули:
AppDataModule:AccountManager,PreferencesManager.AppGatewayModule: биндингиProtocolGateway,SessionCoordinator,IdentityGateway,ProtocolClient.ProtocolGatewayImplиProtocolClientImplделегируют вProtocolRuntime, а не напрямую в UI-слой.
3.2 UI bridge для composable-слоя
UI-композаблы получают зависимости через UiEntryPoint + EntryPointAccessors.fromApplication(...).
UiDependencyAccess.get(...) из ui/* удален (DoD: 0 вхождений).
Для non-Hilt object-ов (CallManager, TransportManager, UpdateManager, utils)
используется ProtocolRuntimeAccess + ProtocolRuntimePort:
- runtime ставится в
RosettaApplicationчерезProtocolRuntimeAccess.install(protocolRuntime); - доступ до install запрещен (fail-fast), чтобы не было тихого отката в
ProtocolManager.
3.3 Разрыв DI-cycle (Hilt)
После перехода на ProtocolRuntime был закрыт цикл зависимостей:
MessageRepository -> ProtocolClient -> ProtocolRuntime -> MessageRepository.
Текущее решение:
ProtocolClientImplполучаетProvider<ProtocolRuntime>(ленивая резолюция).ProtocolRuntimeостается singleton-композицией дляMessageRepository/GroupRepository/AccountManager.- На
assembleDebug/assembleReleaseбольше нетDagger/DependencyCycle.
4. Session lifecycle: единый source of truth
4.1 Модель состояния
SessionState:
LoggedOutAuthInProgress(publicKey?, reason)Ready(account, reason)
4.2 Модель событий
SessionAction:
LoggedOutAuthInProgressReadySyncFromCachedAccount
4.3 Контур изменения состояния
- Только
SessionStoreвладеетMutableStateFlow<SessionState>. - Только
SessionReducerвычисляет next-state. SessionCoordinator/AppSessionCoordinatorбольше не мутируют состояние напрямую, а делаютdispatch(action).SessionStore.dispatch(...)синхронно обновляетIdentityStoreдля консистентности account/profile/auth-runtime.
flowchart LR
A["AuthFlow / MainActivity / Unlock / SetPassword"] --> B["SessionCoordinator.dispatch(action)"]
B --> C["SessionStore.dispatch(action)"]
C --> D["SessionReducer.reduce(current, action)"]
D --> E["StateFlow<SessionState>"]
C --> F["IdentityStore sync"]
4.4 State machine
stateDiagram-v2
[*] --> LoggedOut
LoggedOut --> AuthInProgress: dispatch(AuthInProgress)
AuthInProgress --> Ready: dispatch(Ready)
AuthInProgress --> LoggedOut: dispatch(LoggedOut)
Ready --> LoggedOut: dispatch(LoggedOut)
Ready --> Ready: dispatch(SyncFromCachedAccount(account))
5. Network orchestration после декомпозиции
ProtocolRuntime — DI-фасад runtime слоя.
ProtocolRuntimeCore содержит runtime state machine и делегирует отдельные зоны ответственности:
ConnectionOrchestrator: connect/reconnect/authenticate + network-aware поведение.BootstrapCoordinator: пересчет lifecycle (AUTHENTICATED/BOOTSTRAPPING/READY) и работа сReadyPacketGate.SyncCoordinator: sync state machine (request/timeout, BATCH_START/BATCH_END/NOT_NEEDED, foreground/manual sync).PresenceTypingService: in-memory typing presence с TTL и snapshotStateFlow.PacketRouter: user/search cache + resolve/search continuation routing.OwnProfileSyncService: применение собственного профиля из search и синхронизацияIdentityStore.RetryQueueService: retry очереди отправкиPacketMessage.AuthBootstrapCoordinator: session-aware post-auth bootstrap (transport/update/profile/sync/push).NetworkReconnectWatcher: единый watcher ожидания сети и fast-reconnect триггеры.DeviceVerificationService: состояние списка устройств + pending verification + resolve packets.DeviceRuntimeService: device-id/handshake device + device verification orchestration.CallSignalBridge: call/webrtc/ice signal send+subscribe bridge.PacketSubscriptionRegistry: централизованные подписки на пакеты и fan-out.OutgoingMessagePipelineService: отправкаPacketMessageс retry/error policy.
flowchart TB
PM["ProtocolRuntimeCore"] --> CO["ConnectionOrchestrator"]
PM --> BC["BootstrapCoordinator"]
PM --> SC["SyncCoordinator"]
PM --> PT["PresenceTypingService"]
PM --> PR["PacketRouter"]
PM --> OPS["OwnProfileSyncService"]
PM --> RQ["RetryQueueService"]
PM --> ABC["AuthBootstrapCoordinator"]
PM --> NRW["NetworkReconnectWatcher"]
PM --> DVS["DeviceVerificationService"]
PM --> CSB["CallSignalBridge"]
PM --> PSR["PacketSubscriptionRegistry"]
PM --> SUP["ProtocolConnectionSupervisor"]
PM --> RPG["ReadyPacketGate"]
PM --> P["Protocol (WebSocket + packet codec)"]
6. Централизация packet-subscriptions
Проблема дублирующихся low-level подписок закрыта через PacketSubscriptionRegistry:
- На каждый
packetIdсоздается один bus и один bridge наProtocol.waitPacket(...). - Дальше packet fan-out идет в:
- callback API (
waitPacket/unwaitPacket), SharedFlow(packetFlow(packetId)).
sequenceDiagram
participant Feature as Feature/Service
participant PM as ProtocolRuntimeCore
participant REG as PacketSubscriptionRegistry
participant P as Protocol
Feature->>PM: waitPacket(0x03, callback)
PM->>REG: addCallback(0x03, callback)
REG->>P: waitPacket(0x03, protocolBridge) [once per packetId]
P-->>REG: Packet(0x03)
REG-->>Feature: callback(packet)
REG-->>Feature: packetFlow(0x03).emit(packet)
7. Отправка сообщений: use-cases + retry
Из ChatViewModel выделены use-cases:
SendTextMessageUseCaseSendMediaMessageUseCaseSendForwardUseCase
Текущий поток:
- ViewModel готовит command и шифрованный payload.
- UseCase собирает
PacketMessage. - UseCase вызывает
protocolGateway.sendMessageWithRetry(packet). ProtocolRuntimeCoreрегистрирует пакет вRetryQueueServiceи отправляет в сеть.- Если lifecycle еще не
READY, пакет попадает вReadyPacketGateи flush послеREADY.
flowchart LR
VM["ChatViewModel"] --> UC["Send*UseCase"]
UC --> GW["ProtocolGateway.sendMessageWithRetry"]
GW --> PM["ProtocolRuntimeCore"]
PM --> RQ["RetryQueueService"]
PM --> RG["ReadyPacketGate"]
PM --> P["Protocol.sendPacket"]
8. Auth/bootstrap: фактический runtime flow
sequenceDiagram
participant UI as Auth UI (SetPassword/Unlock)
participant SC as SessionCoordinatorImpl
participant SS as SessionStore
participant PG as ProtocolGateway
participant PM as ProtocolRuntimeCore
participant AM as AccountManager
UI->>SC: bootstrapAuthenticatedSession(account, reason)
SC->>SS: dispatch(AuthInProgress)
SC->>PG: initializeAccount(public, private)
SC->>PG: connect()
SC->>PG: authenticate(public, privateHash)
SC->>PG: reconnectNowIfNeeded(...)
SC->>AM: setCurrentAccount(public)
SC->>SS: dispatch(Ready)
PM-->>PM: HANDSHAKE -> AUTHENTICATED -> BOOTSTRAPPING
PM-->>PM: SyncCompleted + OwnProfileResolved
PM-->>PM: connectionLifecycleState = READY
Важно: SessionState.Ready (app-session готова) и connectionLifecycleState = READY (сеть готова) — это разные state-модели.
9. Состояния соединения (network lifecycle)
ProtocolRuntimeCore.connectionLifecycleState:
DISCONNECTEDCONNECTINGHANDSHAKINGAUTHENTICATEDBOOTSTRAPPINGREADYDEVICE_VERIFICATION_REQUIRED
stateDiagram-v2
[*] --> DISCONNECTED
DISCONNECTED --> CONNECTING
CONNECTING --> HANDSHAKING
HANDSHAKING --> DEVICE_VERIFICATION_REQUIRED
HANDSHAKING --> AUTHENTICATED
AUTHENTICATED --> BOOTSTRAPPING
BOOTSTRAPPING --> READY
READY --> DISCONNECTED
DEVICE_VERIFICATION_REQUIRED --> CONNECTING
10. Ключевые файлы новой архитектуры
app/src/main/java/com/rosetta/messenger/di/AppContainer.ktapp/src/main/java/com/rosetta/messenger/di/UiEntryPoint.ktapp/src/main/java/com/rosetta/messenger/network/ProtocolRuntime.ktapp/src/main/java/com/rosetta/messenger/network/ProtocolRuntimeCore.ktapp/src/main/java/com/rosetta/messenger/network/ProtocolClient.ktapp/src/main/java/com/rosetta/messenger/network/ProtocolRuntimeAccess.ktapp/src/main/java/com/rosetta/messenger/session/AppSessionCoordinator.ktapp/src/main/java/com/rosetta/messenger/session/SessionStore.ktapp/src/main/java/com/rosetta/messenger/session/SessionReducer.ktapp/src/main/java/com/rosetta/messenger/session/SessionAction.ktapp/src/main/java/com/rosetta/messenger/session/IdentityStore.ktapp/src/main/java/com/rosetta/messenger/network/ProtocolManager.ktapp/src/main/java/com/rosetta/messenger/network/PacketSubscriptionRegistry.ktapp/src/main/java/com/rosetta/messenger/network/ProtocolConnectionModels.ktapp/src/main/java/com/rosetta/messenger/network/ProtocolConnectionSupervisor.ktapp/src/main/java/com/rosetta/messenger/network/ReadyPacketGate.ktapp/src/main/java/com/rosetta/messenger/network/connection/ConnectionOrchestrator.ktapp/src/main/java/com/rosetta/messenger/network/connection/BootstrapCoordinator.ktapp/src/main/java/com/rosetta/messenger/network/connection/SyncCoordinator.ktapp/src/main/java/com/rosetta/messenger/network/connection/PresenceTypingService.ktapp/src/main/java/com/rosetta/messenger/network/connection/AuthBootstrapCoordinator.ktapp/src/main/java/com/rosetta/messenger/network/connection/NetworkReconnectWatcher.ktapp/src/main/java/com/rosetta/messenger/network/connection/DeviceVerificationService.ktapp/src/main/java/com/rosetta/messenger/network/connection/DeviceRuntimeService.ktapp/src/main/java/com/rosetta/messenger/network/connection/CallSignalBridge.ktapp/src/main/java/com/rosetta/messenger/network/connection/OutgoingMessagePipelineService.ktapp/src/main/java/com/rosetta/messenger/network/connection/PacketRouter.ktapp/src/main/java/com/rosetta/messenger/network/connection/OwnProfileSyncService.ktapp/src/main/java/com/rosetta/messenger/network/connection/RetryQueueService.ktapp/src/main/java/com/rosetta/messenger/ui/chats/usecase/SendTextMessageUseCase.ktapp/src/main/java/com/rosetta/messenger/ui/chats/usecase/SendMediaMessageUseCase.ktapp/src/main/java/com/rosetta/messenger/ui/chats/usecase/SendForwardUseCase.kt
11. Что осталось как технический долг
ProtocolRuntimeCoreвсе еще содержит много cross-cutting логики и требует дальнейшей декомпозиции.- UI больше не использует
UiDependencyAccess.get(...), но часть экранов все еще берет зависимости черезUiEntryPoint(следующий шаг: передача зависимостей параметрами/через VM). - DI-адаптеры (
ProtocolGatewayImpl,ProtocolClientImpl) переведены наProtocolRuntime, dependency-cycle закрыт черезProvider<ProtocolRuntime>. - Следующий шаг по network core: продолжить декомпозицию
ProtocolRuntimeCore(например:ProtocolLifecycleLogger,AuthRestoreService,ProtocolTraceService) и сократить фасадProtocolManagerдо полного legacy-режима.