Auto-Secure Network Whitelist System
May 31, 2026 · View on GitHub
Overview
The Auto-Secure Network Whitelist is a session-based mechanism that prevents VPN auto-connection after a user manually disconnects on a network with auto-secure enabled. This prevents the frustrating experience of the VPN immediately reconnecting after the user intentionally disconnects.
Key Behavior: The whitelist is temporary (session-based) and automatically clears when the user changes networks, allowing auto-connect to resume normally when they return to the network later.
User Experience Flow
Scenario 1: User Manual Disconnect on Auto-Secure Network
1. User connected to "Coffee Shop WiFi"
2. Auto-Secure: ON
3. VPN: Connected
4. User taps "Disconnect"
→ Network "Coffee Shop WiFi" added to whitelist
→ Auto-connect BLOCKED while on this network
5. User remains on "Coffee Shop WiFi"
→ VPN stays disconnected (whitelist active)
6. User switches to "Home WiFi"
→ Whitelist CLEARED (network changed)
→ Auto-connect resumes normally
7. User returns to "Coffee Shop WiFi" later
→ Auto-connect works (whitelist was cleared in step 6)
Scenario 2: System Disconnect (Protocol Change)
1. User connected to "Coffee Shop WiFi"
2. Auto-Secure: ON
3. VPN: Connected via OpenVPN
4. User changes protocol to WireGuard in settings
→ System disconnect (protocol switching)
→ Network NOT whitelisted (system action, not user intent)
5. VPN automatically reconnects with WireGuard
→ Auto-connect continues normally
Scenario 3: Auto-Secure Turned OFF
1. User connected to "Coffee Shop WiFi"
2. Auto-Secure: ON → User switches to OFF
3. VPN: Connected
4. Auto-Secure setting change triggers disconnect
→ System disconnect (not user disconnect)
→ Network NOT whitelisted
5. VPN stays disconnected (auto-secure is off)
6. Auto-connect will NOT trigger (feature disabled)
Implementation Details
Key Components
1. DeviceStateManager (base/src/main/java/com/windscribe/vpn/state/DeviceStateManager.kt)
- Tracks whitelist state (in-memory)
- Detects network changes
- Clears whitelist on network change
- Provides whitelist check method
2. WindVpnController (base/src/main/java/com/windscribe/vpn/backend/utils/WindVpnController.kt)
- Whitelist set/clear logic
- Distinguishes user disconnect vs system disconnect
- Service lifecycle optimization (stops service when whitelisted)
3. AutoConnectService (base/src/main/java/com/windscribe/vpn/services/AutoConnectService.kt)
- Checks whitelist before auto-connecting
- Skips auto-connect if current network is whitelisted
4. VpnBackend (base/src/main/java/com/windscribe/vpn/backend/openvpn/VpnBackend.kt)
- System disconnect handling
- Always passes
error = nullfor system disconnects to prevent whitelisting
5. Windscribe.kt (base/src/main/java/com/windscribe/vpn/Windscribe.kt)
- Network change observer
- Preference sync on network change
State Management
Whitelist Data Structure
// DeviceStateManager.kt
private val whitelistedNetworks = mutableSetOf<String>() // SSID or network identifier
fun addToWhitelist(networkId: String) {
whitelistedNetworks.add(networkId)
}
fun clearWhitelist() {
whitelistedNetworks.clear()
}
fun isWhitelisted(networkId: String): Boolean {
return whitelistedNetworks.contains(networkId)
}
Session-Based Lifecycle
- Created: When user manually disconnects on auto-secure enabled network
- Active: While user remains on the same network
- Cleared: Immediately when network changes (WiFi → Mobile, WiFi A → WiFi B, etc.)
- Persisted: NO — in-memory only, lost on app restart
Decision Logic
When to Whitelist
fun onUserDisconnect(currentNetwork: String) {
if (preferencesHelper.isAutoSecureOn) {
deviceStateManager.addToWhitelist(currentNetwork)
stopVpnService() // Optimize: no need to keep service alive
}
}
Conditions:
- ✅ User initiated disconnect (taps disconnect button)
- ✅ Auto-secure is enabled
- ✅ Connected to a network (not airplane mode)
NOT whitelisted:
- ❌ System disconnect (protocol change, auto-secure toggle)
- ❌ Auto-secure is OFF
- ❌ Connection error/failure (different flow)
When to Clear Whitelist
fun onNetworkChanged(oldNetwork: String, newNetwork: String) {
deviceStateManager.clearWhitelist()
checkAutoConnect() // Resume auto-connect logic
}
Triggers:
- ✅ WiFi → Mobile data
- ✅ WiFi A → WiFi B
- ✅ Mobile data → WiFi
- ✅ Network disconnect → Network connect
- ✅ Airplane mode → Network restored
When to Check Whitelist
// AutoConnectService.kt
fun onNetworkAvailable(network: String) {
if (deviceStateManager.isWhitelisted(network)) {
// Skip auto-connect
return
}
if (preferencesHelper.isAutoSecureOn) {
// Proceed with auto-connect
windVpnController.connect()
}
}
Check Points:
- Network change detected (WiFi/mobile data connects)
- Auto-secure setting enabled
- VPN currently disconnected
Code Flow
Flow 1: User Manual Disconnect
User taps "Disconnect" (UI)
↓
HomeViewModel.disconnect() called
↓
WindVpnController.disconnect(isUserInitiated = true)
↓
VpnBackend.stop()
↓
onDisconnected callback
↓
DeviceStateManager.getCurrentNetwork()
↓
PreferencesHelper.isAutoSecureOn → true
↓
DeviceStateManager.addToWhitelist(currentNetwork)
↓
WindVpnService.stopSelf() // Service optimization
↓
UI shows "Disconnected" state
↓
[User remains on same network]
↓
AutoConnectService receives network broadcast
↓
deviceStateManager.isWhitelisted(network) → true
↓
Auto-connect SKIPPED
Flow 2: Network Change (Whitelist Clear)
User switches from WiFi to Mobile Data
↓
DeviceStateReceiverWrapper.onNetworkChanged()
↓
DeviceStateManager.clearWhitelist() // CRITICAL
↓
Windscribe.networkObserver.onNetworkChanged()
↓
AutoConnectService.checkAutoConnect()
↓
deviceStateManager.isWhitelisted(network) → false
↓
preferencesHelper.isAutoSecureOn → true
↓
windVpnController.connect() // Auto-connect resumed
Flow 3: System Disconnect (Protocol Change)
User changes protocol in settings
↓
SettingsViewModel.setProtocol(newProtocol)
↓
WindVpnController.switchProtocol(newProtocol)
↓
VpnBackend.stop(error = null) // error = null indicates system disconnect
↓
onDisconnected callback (error = null)
↓
WindVpnController checks: error == null → System disconnect
↓
NO whitelist action (intentional)
↓
VpnBackend.start(newProtocol)
↓
Auto-reconnect continues normally
Important Notes
Protocol/Port Switching on Network Change
Requires autoConnect Setting Enabled:
The feature that automatically switches protocols or ports based on network change requires the autoConnect preference to be enabled. This is because protocol/port switching is fundamentally an "auto" feature — it automatically adapts the VPN configuration without user intervention.
fun onNetworkChanged(newNetwork: String) {
if (!preferencesHelper.autoConnect) {
// Skip protocol/port switching — not enabled
return
}
val networkProfile = getNetworkProfile(newNetwork)
if (networkProfile.protocol != currentProtocol) {
switchProtocol(networkProfile.protocol)
}
}
User Experience:
- Auto-connect ON + Per-network config → Protocol/port switches automatically on network change
- Auto-connect OFF + Per-network config → User must manually connect after network change (protocol/port switching does not occur)
System Disconnects Always Pass error = null
Critical Implementation Detail:
All system-initiated disconnects (protocol change, auto-secure toggle, port change) MUST pass error = null to the disconnect callback. This signals that the disconnect was intentional and should NOT trigger whitelisting.
// ❌ WRONG — Will whitelist and break auto-reconnect
vpnBackend.stop() // error defaults to VpnError.USER_DISCONNECT
// ✅ CORRECT — System disconnect, no whitelisting
vpnBackend.stop(error = null)
System Disconnect Scenarios:
- Protocol switching (OpenVPN → WireGuard)
- Port switching (UDP → TCP)
- Auto-secure toggle (ON → OFF or OFF → ON)
- Connection mode change (manual → auto)
Service Lifecycle Optimization
When a network is whitelisted (user disconnected on auto-secure network), the WindVpnService can be stopped entirely since auto-connect is blocked for that session.
Benefits:
- ✅ Reduced battery consumption (no background service running)
- ✅ No persistent notification (service not foreground)
- ✅ Better user experience (clear signal VPN is off)
fun onUserDisconnect() {
if (preferencesHelper.isAutoSecureOn) {
deviceStateManager.addToWhitelist(currentNetwork)
stopService(Intent(this, WindVpnService::class.java)) // Stop service
}
}
fun onNetworkChanged() {
deviceStateManager.clearWhitelist()
if (shouldAutoConnect()) {
startService(Intent(this, WindVpnService::class.java)) // Restart service
}
}
Edge Cases & Handling
Edge Case 1: User Disconnects Multiple Times
Scenario: User disconnects, reconnects manually, disconnects again
Handling:
- Each disconnect re-adds network to whitelist (idempotent)
- Whitelist uses
Set<String>, so duplicate adds are no-op - Behavior remains consistent
Edge Case 2: Rapid Network Changes
Scenario: User switches networks rapidly (WiFi A → WiFi B → WiFi A)
Handling:
- Each network change clears whitelist
- When returning to WiFi A, whitelist is already clear
- Auto-connect resumes normally
Edge Case 3: App Restart
Scenario: User disconnects on WiFi, kills app, reopens app on same WiFi
Handling:
- Whitelist is in-memory only (lost on app restart)
- On app reopen, whitelist is empty
- Auto-connect resumes normally (desired behavior — user restarted app, intent reset)
Edge Case 4: Airplane Mode
Scenario: User disconnects on WiFi, enables airplane mode, disables airplane mode
Handling:
- Airplane mode triggers network change (network lost)
- Whitelist cleared when airplane mode enabled
- When airplane mode disabled, auto-connect resumes
Testing
Manual Test Cases
Test 1: Basic Whitelist Behavior
- Connect to WiFi with auto-secure ON
- Tap disconnect
- Verify VPN does not auto-reconnect
- Switch to mobile data
- Verify VPN auto-connects
Test 2: Protocol Switching
- Connect to WiFi with OpenVPN
- Change protocol to WireGuard in settings
- Verify VPN reconnects automatically with WireGuard
- Confirm no whitelisting occurred
Test 3: Auto-Secure Toggle
- Connect to WiFi with auto-secure ON
- Toggle auto-secure OFF
- Verify VPN disconnects
- Verify VPN does not auto-reconnect (feature disabled)
Test 4: Return to Network
- Connect to WiFi A with auto-secure ON
- Tap disconnect
- Switch to WiFi B (auto-connects)
- Return to WiFi A
- Verify VPN auto-connects (whitelist was cleared)
Automated Test Cases
// DeviceStateManagerTest.kt
@Test
fun `whitelist is cleared on network change`() {
val manager = DeviceStateManager()
manager.addToWhitelist("WiFi_A")
assertTrue(manager.isWhitelisted("WiFi_A"))
manager.onNetworkChanged("WiFi_A", "WiFi_B")
assertFalse(manager.isWhitelisted("WiFi_A"))
assertFalse(manager.isWhitelisted("WiFi_B"))
}
@Test
fun `system disconnect does not whitelist`() {
val controller = WindVpnController()
controller.disconnect(error = null) // System disconnect
assertFalse(deviceStateManager.isWhitelisted(currentNetwork))
}
@Test
fun `user disconnect whitelists network`() {
val controller = WindVpnController()
preferencesHelper.isAutoSecureOn = true
controller.disconnect(isUserInitiated = true)
assertTrue(deviceStateManager.isWhitelisted(currentNetwork))
}
Troubleshooting
Problem: Auto-connect not working after returning to network
Diagnosis:
adb logcat | grep -i "whitelist"
Common Causes:
- Whitelist not cleared on network change
DeviceStateManager.onNetworkChanged()not called- Network identifier mismatch (SSID vs network hash)
Fix: Ensure Windscribe.networkObserver properly calls deviceStateManager.clearWhitelist() on network change.
Problem: VPN auto-reconnects immediately after user disconnect
Diagnosis:
adb logcat | grep -E "(AutoConnect|DeviceState)"
Common Causes:
- System disconnect passed instead of user disconnect
- Auto-secure preference not checked before whitelisting
- Whitelist check skipped in
AutoConnectService
Fix: Verify WindVpnController.disconnect() receives correct isUserInitiated flag.
Problem: Protocol switching fails to auto-reconnect
Diagnosis:
adb logcat | grep -E "(VpnBackend|WindVpnController)"
Common Causes:
error != nullpassed on system disconnect (triggers whitelisting)- Auto-connect setting disabled
- Protocol switching logic broken
Fix: Ensure VpnBackend.stop(error = null) for all system disconnects.
References
- AGENTS.md — Architecture overview
- DeviceStateManager.kt — Implementation
- WindVpnController.kt — Disconnect logic
- AutoConnectService.kt — Auto-connect logic
Last Updated: 2026-04-22 Feature Version: Introduced in v3.60 Maintained By: Engineering Team