android: VpnStateService - single retry countdown, isolated listeners
- remove pending RETRY_MSG on reset/set: a Retry tap during a backoff started a second countdown chain (double-speed ticks, two reconnects) - notify listeners over a copy with per-listener error handling - disconnect() tolerates the VPN service not running Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
358dcb94b2
commit
f2e4ffe860
1 file changed
+39
-14
+39
-14
@@ -41,6 +41,7 @@ import org.strongswan.android.ui.VpnProfileControlActivity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -199,6 +200,27 @@ public class VpnStateService extends Service
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all registered listeners about a state change. Iterates over a
|
||||
* copy (a listener may unregister itself from its callback) and isolates
|
||||
* the listeners from each other: one failing listener must neither skip
|
||||
* the others nor crash the main thread.
|
||||
*/
|
||||
private void notifyStateListeners()
|
||||
{
|
||||
for (VpnStateListener listener : new ArrayList<>(mListeners))
|
||||
{
|
||||
try
|
||||
{
|
||||
listener.stateChanged();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
DiagLog.i(TAG, "state listener failed: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current VPN profile.
|
||||
*
|
||||
@@ -373,7 +395,15 @@ public class VpnStateService extends Service
|
||||
Context context = getApplicationContext();
|
||||
Intent intent = new Intent(context, CharonVpnService.class);
|
||||
intent.setAction(CharonVpnService.DISCONNECT_ACTION);
|
||||
context.startService(intent);
|
||||
try
|
||||
{
|
||||
context.startService(intent);
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{ /* background start of a service that is not running (Android 8+):
|
||||
* without the service there is no tunnel to tear down */
|
||||
DiagLog.i(TAG, "disconnect: VPN service not running (" + e.getMessage() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -589,10 +619,7 @@ public class VpnStateService extends Service
|
||||
{
|
||||
if (change.call())
|
||||
{ /* otherwise there is no need to notify the listeners */
|
||||
for (VpnStateListener listener : mListeners)
|
||||
{
|
||||
listener.stateChanged();
|
||||
}
|
||||
notifyStateListeners();
|
||||
/* the home screen widget mirrors the state */
|
||||
VpnProfile widgetProfile = mProfile;
|
||||
ShxWidgetState.publish(VpnStateService.this, mState, getErrorState(),
|
||||
@@ -602,7 +629,7 @@ public class VpnStateService extends Service
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
DiagLog.i(TAG, "state change failed: " + e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -766,6 +793,9 @@ public class VpnStateService extends Service
|
||||
mRetryTimeout = mRetryIn = mTimeoutProvider.getTimeout(error);
|
||||
mRetryFrozen = false;
|
||||
DiagLog.i(TAG, "error " + error + ", retry in " + (mRetryTimeout / 1000) + "s");
|
||||
/* never run two countdown chains in parallel (double-speed countdown,
|
||||
* double notification updates and two reconnects at zero) */
|
||||
mHandler.removeMessages(RETRY_MSG);
|
||||
if (mRetryTimeout <= 0)
|
||||
{
|
||||
return;
|
||||
@@ -778,6 +808,7 @@ public class VpnStateService extends Service
|
||||
*/
|
||||
private void resetRetryTimer()
|
||||
{
|
||||
mHandler.removeMessages(RETRY_MSG);
|
||||
mRetryTimeout = 0;
|
||||
mRetryIn = 0;
|
||||
mRetryFrozen = false;
|
||||
@@ -858,10 +889,7 @@ public class VpnStateService extends Service
|
||||
/* calculate next interval before notifying listeners */
|
||||
long next = SystemClock.uptimeMillis() + retryStep(service.mRetryIn);
|
||||
|
||||
for (VpnStateListener listener : service.mListeners)
|
||||
{
|
||||
listener.stateChanged();
|
||||
}
|
||||
service.notifyStateListeners();
|
||||
sendMessageAtTime(obtainMessage(RETRY_MSG), next);
|
||||
}
|
||||
else
|
||||
@@ -872,10 +900,7 @@ public class VpnStateService extends Service
|
||||
* ticking every second */
|
||||
service.mRetryFrozen = true;
|
||||
service.mRetryIn = RETRY_SUPPRESS_MS;
|
||||
for (VpnStateListener listener : service.mListeners)
|
||||
{
|
||||
listener.stateChanged();
|
||||
}
|
||||
service.notifyStateListeners();
|
||||
sendMessageAtTime(obtainMessage(RETRY_MSG), SystemClock.uptimeMillis() + RETRY_SUPPRESS_MS);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user