android: Change state handling to display errors occurring while the app is hidden
A new connection ID allows listeners to track which errors they have already shown to the user or were already dismissed by the user. This was necessary because the state fragment is now unregistered from state changes when it is not shown.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public class VpnStateService extends Service
|
||||
{
|
||||
private final List<VpnStateListener> mListeners = new ArrayList<VpnStateListener>();
|
||||
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<Boolean>() {
|
||||
@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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<RemediationInstruction> 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())
|
||||
|
||||
Reference in New Issue
Block a user