diff --git a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java index 305ee80b2..853de561e 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java @@ -190,7 +190,6 @@ public class CharonVpnService extends VpnService implements Runnable stopCurrentConnection(); if (mNextProfile == null) { - setProfile(null); setState(State.DISABLED); if (mTerminate) { @@ -207,10 +206,7 @@ public class CharonVpnService extends VpnService implements Runnable mCurrentCertificateAlias = mCurrentProfile.getCertificateAlias(); mCurrentUserCertificateAlias = mCurrentProfile.getUserCertificateAlias(); - setProfile(mCurrentProfile); - setError(ErrorState.NO_ERROR); - setState(State.CONNECTING); - setImcState(ImcState.UNKNOWN); + startConnection(mCurrentProfile); mIsDisconnecting = false; BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName()); @@ -258,17 +254,18 @@ public class CharonVpnService extends VpnService implements Runnable } /** - * Update the VPN profile on the state service. Called by the handler thread. + * Notify the state service about a new connection attempt. + * Called by the handler thread. * * @param profile currently active VPN profile */ - private void setProfile(VpnProfile profile) + private void startConnection(VpnProfile profile) { synchronized (mServiceLock) { if (mService != null) { - mService.setProfile(profile); + mService.startConnection(profile); } } } @@ -337,9 +334,9 @@ public class CharonVpnService extends VpnService implements Runnable { if (mService != null) { - mService.setError(error); if (!mIsDisconnecting) { + mService.setError(error); mService.disconnect(); } } diff --git a/src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java b/src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java index 2c530bad4..3b75a8f0b 100644 --- a/src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java +++ b/src/frontends/android/src/org/strongswan/android/logic/VpnStateService.java @@ -36,6 +36,7 @@ public class VpnStateService extends Service { private final List mListeners = new ArrayList(); private final IBinder mBinder = new LocalBinder(); + private long mConnectionID = 0; private Handler mHandler; private VpnProfile mProfile; private State mState = State.DISABLED; @@ -132,6 +133,19 @@ public class VpnStateService extends Service return mProfile; } + /** + * Get the current connection ID. May be used to track which state + * changes have already been handled. + * + * Is increased when startConnection() is called. + * + * @return connection ID + */ + public long getConnectionID() + { /* only updated from the main thread so no synchronization needed */ + return mConnectionID; + } + /** * Get the current state. * @@ -223,21 +237,26 @@ public class VpnStateService extends Service } /** - * Set the VPN profile currently active. Listeners are not notified. + * Called when a connection is started. Sets the currently active VPN + * profile, resets IMC and Error state variables, sets the State to + * CONNECTING, increases the connection ID, and notifies all listeners. * * May be called from threads other than the main thread. * * @param profile current profile */ - public void setProfile(final VpnProfile profile) + public void startConnection(final VpnProfile profile) { - /* even though we don't notify the listeners the update is done from the - * same handler so updates are predictable for listeners */ - mHandler.post(new Runnable() { + notifyListeners(new Callable() { @Override - public void run() + public Boolean call() throws Exception { + VpnStateService.this.mConnectionID++; VpnStateService.this.mProfile = profile; + VpnStateService.this.mState = State.CONNECTING; + VpnStateService.this.mError = ErrorState.NO_ERROR; + VpnStateService.this.mImcState = ImcState.UNKNOWN; + return true; } }); } diff --git a/src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java b/src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java index 88f8df377..d1394f65a 100644 --- a/src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java +++ b/src/frontends/android/src/org/strongswan/android/ui/VpnStateFragment.java @@ -49,9 +49,8 @@ import android.widget.TextView; public class VpnStateFragment extends Fragment implements VpnStateListener { - private static final String KEY_ERROR = "error"; - private static final String KEY_IMC_STATE = "imc_state"; - private static final String KEY_NAME = "name"; + private static final String KEY_ERROR_CONNECTION_ID = "error_connection_id"; + private static final String KEY_DISMISSED_CONNECTION_ID = "dismissed_connection_id"; private TextView mProfileNameView; private TextView mProfileView; @@ -59,11 +58,9 @@ public class VpnStateFragment extends Fragment implements VpnStateListener private int stateBaseColor; private Button mActionButton; private ProgressDialog mProgressDialog; - private State mState; private AlertDialog mErrorDialog; - private ErrorState mError; - private ImcState mImcState; - private String mErrorProfileName; + private long mErrorConnectionID; + private long mDismissedConnectionID; private VpnStateService mService; private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override @@ -91,13 +88,12 @@ public class VpnStateFragment extends Fragment implements VpnStateListener context.bindService(new Intent(context, VpnStateService.class), mServiceConnection, Service.BIND_AUTO_CREATE); - mError = ErrorState.NO_ERROR; - mImcState = ImcState.UNKNOWN; - if (savedInstanceState != null && savedInstanceState.containsKey(KEY_ERROR)) + mErrorConnectionID = 0; + mDismissedConnectionID = 0; + if (savedInstanceState != null && savedInstanceState.containsKey(KEY_ERROR_CONNECTION_ID)) { - mError = (ErrorState)savedInstanceState.getSerializable(KEY_ERROR); - mImcState = (ImcState)savedInstanceState.getSerializable(KEY_IMC_STATE); - mErrorProfileName = savedInstanceState.getString(KEY_NAME); + mErrorConnectionID = (Long)savedInstanceState.getSerializable(KEY_ERROR_CONNECTION_ID); + mDismissedConnectionID = (Long)savedInstanceState.getSerializable(KEY_DISMISSED_CONNECTION_ID); } } @@ -106,9 +102,8 @@ public class VpnStateFragment extends Fragment implements VpnStateListener { super.onSaveInstanceState(outState); - outState.putSerializable(KEY_ERROR, mError); - outState.putSerializable(KEY_IMC_STATE, mImcState); - outState.putString(KEY_NAME, mErrorProfileName); + outState.putSerializable(KEY_ERROR_CONNECTION_ID, mErrorConnectionID); + outState.putSerializable(KEY_DISMISSED_CONNECTION_ID, mDismissedConnectionID); } @Override @@ -179,37 +174,27 @@ public class VpnStateFragment extends Fragment implements VpnStateListener public void updateView() { + long connectionID = mService.getConnectionID(); + VpnProfile profile = mService.getProfile(); State state = mService.getState(); - ErrorState error = ErrorState.NO_ERROR; - ImcState imcState = ImcState.UNKNOWN; + ErrorState error = mService.getErrorState(); + ImcState imcState = mService.getImcState(); String name = "", gateway = ""; - if (state != State.DISABLED) + if (profile != null) { - VpnProfile profile = mService.getProfile(); - if (profile != null) - { - name = profile.getName(); - gateway = profile.getGateway(); - } - error = mService.getErrorState(); - imcState = mService.getImcState(); + name = profile.getName(); + gateway = profile.getGateway(); } - if (reportError(name, state, error, imcState)) + if (reportError(connectionID, name, error, imcState)) { return; } - if (state == mState) - { /* avoid unnecessary updates */ - return; - } - hideProgressDialog(); enableActionButton(false); mProfileNameView.setText(name); - mState = state; switch (state) { @@ -239,19 +224,11 @@ public class VpnStateFragment extends Fragment implements VpnStateListener } } - private boolean reportError(String name, State state, ErrorState error, ImcState imcState) + private boolean reportError(long connectionID, String name, ErrorState error, ImcState imcState) { - if (mError != ErrorState.NO_ERROR) - { /* we are currently reporting an error which was not yet dismissed */ - error = mError; - imcState = mImcState; - name = mErrorProfileName; - } - else if (error != ErrorState.NO_ERROR && (state == State.CONNECTING || state == State.CONNECTED)) - { /* while initiating we report errors */ - mError = error; - mImcState = imcState; - mErrorProfileName = name; + if (connectionID > mDismissedConnectionID) + { /* report error if it hasn't been dismissed yet */ + mErrorConnectionID = connectionID; } else { /* ignore all other errors */ @@ -332,8 +309,7 @@ public class VpnStateFragment extends Fragment implements VpnStateListener private void clearError() { - mError = ErrorState.NO_ERROR; - mImcState = ImcState.UNKNOWN; + mDismissedConnectionID = mErrorConnectionID; updateView(); } @@ -371,7 +347,7 @@ public class VpnStateFragment extends Fragment implements VpnStateListener private void showErrorDialog(int textid) { final List instructions = mService.getRemediationInstructions(); - final boolean show_instructions = mImcState == ImcState.BLOCK && !instructions.isEmpty(); + final boolean show_instructions = mService.getImcState() == ImcState.BLOCK && !instructions.isEmpty(); int text = show_instructions ? R.string.show_remediation_instructions : R.string.show_log; mErrorDialog = new AlertDialog.Builder(getActivity())