diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/HomeViewModel.kt b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/HomeViewModel.kt index d9311936b..2488597d3 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/HomeViewModel.kt +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/compose/HomeViewModel.kt @@ -199,7 +199,14 @@ class HomeViewModel(app: Application) : AndroidViewModel(app), VpnStateService.V { synchronized(underlyingNetworks) { - underlyingNetworks[network] = capabilities + val previous = underlyingNetworks.put(network, capabilities) + /* fired on every signal-strength tick: the published state (network + * kinds, SSID) only depends on the transports, and reading the SSID + * is a system call, so unchanged transports end here */ + if (previous != null && sameTransports(previous, capabilities)) + { + return + } publishNetworks() } } @@ -214,6 +221,10 @@ class HomeViewModel(app: Application) : AndroidViewModel(app), VpnStateService.V } } + private fun sameTransports(a: NetworkCapabilities, b: NetworkCapabilities): Boolean = + a.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == b.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && + a.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == b.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + private fun publishNetworks() { _networkAvailable.value = underlyingNetworks.isNotEmpty() diff --git a/src/frontends/android/docs/energy-plan.md b/src/frontends/android/docs/energy-plan.md index ac5918f7f..201e1c13a 100644 --- a/src/frontends/android/docs/energy-plan.md +++ b/src/frontends/android/docs/energy-plan.md @@ -69,11 +69,13 @@ Что остаётся: замерить число будильников (`dumpsys alarm`) за ночь и, если вклад велик, добавить в профиль **необязательную** настройку, не меняя умолчание; рекомендовать значения по результатам теста на конкретной сети пользователя. -### C. Единый хаб сетевых колбэков (средний эффект, средняя сложность) -`registerNetworkCallback` вызывают минимум семь компонентов: `CharonVpnService`, `NetworkManager`, `NetworkMonitor`, -`WhiteListManager`, `HealthCheckManager`, `PauseManager`, `HomeViewModel`. Каждое изменение сети будит их все и дублирует -работу (запросы SSID, перепроверки). Сделать один подписчик в приложении, раздающий состояние через `StateFlow`; -остальные подписываются на него. Попутно уменьшает риск лимита колбэков (в `HomeViewModel` он уже обрабатывается `catch`). +### C. Сетевые колбэки — единый хаб отклонён, исправлен один шумный +Разбор показал, что хаб не нужен: `NetworkMonitor` и `WhiteListManager` уже отсекают тики уровня сигнала в `onCapabilitiesChanged`, +`PauseManager` регистрируется только на время паузы, проверка здоровья — только при включённой функции и соединённом VPN, +а `NetworkManager` и `UnderlyingNetworkTracker` в `CharonVpnService` — часть самого туннеля (трогать нельзя по разделу 5). +Выигрыш от объединения — единицы вызовов Binder на смену сети при заметном риске для автоподключения и белых списков. +Исправлено единственное реальное место: `HomeViewModel.onCapabilitiesChanged` на каждый тик сигнала Wi-Fi заново читал SSID (системный вызов); +теперь состояние пересчитывается только при смене транспорта. ### D. Опрос переднего приложения в сценариях (средний эффект, низкая сложность) `ScenarioEngine.poll` — каждые 4 с `UsageStatsManager.queryEvents` (IPC), пока есть сценарий с триггером «приложение».