Skip to content

fix(service): prevent notification flickering when auto-tunnel is active#1196

Open
naonak wants to merge 1 commit intowgtunnel:masterfrom
naonak:fix/notification-flicker-auto-tunnel
Open

fix(service): prevent notification flickering when auto-tunnel is active#1196
naonak wants to merge 1 commit intowgtunnel:masterfrom
naonak:fix/notification-flicker-auto-tunnel

Conversation

@naonak
Copy link
Contributor

@naonak naonak commented Mar 11, 2026

Summary

Fix two issues causing the "Tunnel running" notification (ID 100) to flicker visually when auto-tunnel is active (I have Android 16).

Problem

When both the VPN notification (ID 100) and the auto-tunnel notification (ID 122) are visible in the notification shade simultaneously:

  1. Race condition on service lifecycle: onDestroy() in AutoTunnelService and BaseTunnelForegroundService called STOP_FOREGROUND_REMOVE, which globally cancels the notification ID. During a rapid stop/start cycle (e.g. network transition), this could remove a notification already posted by the newly started service instance, causing Android 16 to kill the foreground service that lost its notification.

  2. Notification drawer flicker: The statsJob updated the VPN notification every second via NotificationManager.notify(). Each update triggers a full re-layout of the notification shade. With two stacked notifications from the same app, this causes both to visually jump every second.

Solution

  • Changed onDestroy() in both services to use STOP_FOREGROUND_DETACH instead of STOP_FOREGROUND_REMOVE. The intentional stop path (stop()) still uses STOP_FOREGROUND_REMOVE before stopSelf() to cleanly remove the notification.
  • Switched statsJob from NotificationManager.notify() to ServiceCompat.startForeground() (correct API for updating a foreground service notification).
  • Only update the notification when traffic stats actually change (skip re-layouts during idle periods).
  • Reduced the stats poll interval from 1s to 5s.

Technical Design

Files changed:

  • AutoTunnelService.kt: Added STOP_FOREGROUND_REMOVE in stop() before stopSelf(); changed onDestroy() to STOP_FOREGROUND_DETACH.
  • BaseTunnelForegroundService.kt: Changed onDestroy() to STOP_FOREGROUND_DETACH; refactored statsJob to use startForeground(), conditional updates, and 5s interval.

Before / After (statsJob):

// Before
notificationManager.show(VPN_NOTIFICATION_ID, notification)
delay(1000)

// After
var lastTraffic: Pair<Long, Long>? = null
if (traffic != lastTraffic) {
    lastTraffic = traffic
    ServiceCompat.startForeground(this@..., VPN_NOTIFICATION_ID, notification, fgsType)
}
delay(5000)

Test Plan

  • Auto-tunnel active + tunnel running → both notifications visible → no flickering at rest
  • Active traffic → notification updates at most every 5s, no jarring jump
  • Tunnel idle (no traffic) → zero notification updates
  • Disable auto-tunnel → VPN notification disappears cleanly
  • Network transition with auto-tunnel → stop/start cycle completes without leaving a stale notification

Two separate issues fixed:

1. Race condition in foreground service lifecycle (REMOVE vs DETACH):
   onDestroy() was calling STOP_FOREGROUND_REMOVE which globally cancels
   the notification ID, potentially removing a notification already posted
   by a newly started service instance. Changed to STOP_FOREGROUND_DETACH
   in both AutoTunnelService and BaseTunnelForegroundService.onDestroy().
   The intentional stop path (stop()) still uses STOP_FOREGROUND_REMOVE
   before stopSelf() to cleanly remove the notification.

2. Notification drawer re-layout causing visual flicker:
   The statsJob was updating the VPN notification every second via
   notify(). On Android 16, each update triggers a re-layout of the
   notification shade, causing both the VPN and auto-tunnel notifications
   to visually jump when stacked. Fixed by:
   - Using startForeground() instead of notify() (correct API for FGS)
   - Only updating when traffic stats actually change
   - Reducing interval from 1s to 5s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant