Skip to content

Commit 629432e

Browse files
committed
android: drop health warnings in Stopped and NeedsLogin state
fixes tailscale/corp#36233 We should be dropping health warnings when we're not in the Running state. This also adds some functionality to inject fake health warnings to make it possible to trigger these without triggering a real health condition. Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
1 parent 80320dc commit 629432e

3 files changed

Lines changed: 69 additions & 16 deletions

File tree

android/src/main/java/com/tailscale/ipn/ui/notifier/HealthNotifier.kt

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class HealthNotifier(
3434
const val HEALTH_CHANNEL_ID = "tailscale-health"
3535
}
3636

37-
private val TAG = "Health"
37+
private val TAG = "health"
3838
private val ignoredWarnableCodes: Set<String> =
3939
setOf(
4040
// Ignored on Android because installing unstable takes quite some effort
@@ -45,26 +45,27 @@ class HealthNotifier(
4545
"wantrunning-false")
4646

4747
init {
48+
// This roughly matches the iOS/macOS implementation in terms of debouncing, and ingoring
49+
// health warnings in various states.
4850
scope.launch {
4951
healthStateFlow
5052
.distinctUntilChanged { old, new -> old?.Warnings?.count() == new?.Warnings?.count() }
5153
.combine(ipnStateFlow, ::Pair)
52-
.debounce(5000)
54+
.debounce(3000)
5355
.collect { pair ->
5456
val health = pair.first
55-
val ipnState = pair.second
56-
// When the client is Stopped, no warnings should get added, and any warnings added
57-
// previously should be removed.
58-
if (ipnState == Ipn.State.Stopped) {
59-
TSLog.d(
60-
TAG,
61-
"Ignoring and dropping all pre-existing health messages in the Stopped state")
62-
dropAllWarnings()
63-
return@collect
64-
} else {
65-
TSLog.d(TAG, "Health updated: ${health?.Warnings?.keys?.sorted()}")
66-
health?.Warnings?.let {
67-
notifyHealthUpdated(it.values.mapNotNull { it }.toTypedArray())
57+
// Only deliver health notifications when the client is Running
58+
when (val ipnState = pair.second) {
59+
Ipn.State.Running -> {
60+
TSLog.d(TAG, "Health updated: ${health?.Warnings?.keys?.sorted()}")
61+
health?.Warnings?.let {
62+
notifyHealthUpdated(it.values.mapNotNull { it }.toTypedArray())
63+
}
64+
}
65+
else -> {
66+
TSLog.d(TAG, "Ignoring and dropping all health messages in state ${ipnState}")
67+
dropAllWarnings()
68+
return@collect
6869
}
6970
}
7071
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.tailscale.ipn.ui.notifier
2+
3+
import com.tailscale.ipn.ui.model.Health
4+
import com.tailscale.ipn.ui.util.set
5+
import kotlinx.coroutines.flow.MutableStateFlow
6+
7+
/**
8+
* Injects a fake Health.State for testing purposes.
9+
* This bypasses the normal IPN bus notification flow.
10+
*/
11+
fun Notifier.injectFakeHealthState(
12+
includeHighSeverity: Boolean = true,
13+
includeConnectivityImpact: Boolean = false,
14+
customWarnings: List<Health.UnhealthyState> = emptyList()
15+
) {
16+
val warnings = mutableMapOf<String, Health.UnhealthyState?>()
17+
18+
if (includeHighSeverity) {
19+
warnings["test-high-severity"] = Health.UnhealthyState(
20+
WarnableCode = "test-high-severity",
21+
Severity = Health.Severity.high,
22+
Title = "Test High Severity Warning",
23+
Text = "This is a test warning with high severity",
24+
ImpactsConnectivity = includeConnectivityImpact,
25+
DependsOn = null
26+
)
27+
}
28+
29+
warnings["test-low-severity"] = Health.UnhealthyState(
30+
WarnableCode = "test-low-severity",
31+
Severity = Health.Severity.low,
32+
Title = "Test Low Severity Warning",
33+
Text = "This is a test warning with low severity",
34+
ImpactsConnectivity = false,
35+
DependsOn = null
36+
)
37+
38+
customWarnings.forEach { warning ->
39+
warnings[warning.WarnableCode] = warning
40+
}
41+
42+
(health as MutableStateFlow).set(Health.State(Warnings = warnings))
43+
}

android/src/main/java/com/tailscale/ipn/ui/notifier/Notifier.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import kotlinx.serialization.ExperimentalSerializationApi
2020
import kotlinx.serialization.json.Json
2121
import kotlinx.serialization.json.decodeFromStream
2222

23+
// When set to true, the Notifier will inject fake health warnings for testing purposes
24+
val INJECT_FAKE_HEALTH_WARNINGS = false
25+
2326
// Notifier is a wrapper around the IPN Bus notifier. It provides a way to watch
2427
// for changes in various parts of the Tailscale engine. You will typically only use
2528
// a single Notifier per instance of your application which lasts for the lifetime of
@@ -86,7 +89,13 @@ object Notifier {
8689
notify.OutgoingFiles?.let(outgoingFiles::set)
8790
notify.FilesWaiting?.let(filesWaiting::set)
8891
notify.IncomingFiles?.let(incomingFiles::set)
89-
notify.Health?.let(health::set)
92+
notify.Health?.let {
93+
if (INJECT_FAKE_HEALTH_WARNINGS) {
94+
injectFakeHealthState()
95+
} else {
96+
health.set(it)
97+
}
98+
}
9099
}
91100
}
92101
}

0 commit comments

Comments
 (0)