package com.swisscom.safeconnect.widget;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.VpnService;
import android.os.Bundle;
import android.os.IBinder;
import com.swisscom.safeconnect.backend.BackendConnector;
import com.swisscom.safeconnect.model.PlumberAuthResponse;
import com.swisscom.safeconnect.utils.Config;
import com.swisscom.safeconnect.utils.VpnConfigurator;
import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.logic.CharonVpnService;
import org.strongswan.android.logic.VpnStateService;
/**
* Created by cianci on 1/13/15.
*
* This activity just exists because we sometimes need to show the confirmation
* dialog for the vpn connection. This cannot be done directly from the widget itself.
*
* The activity has @android:style/Theme.NoDisplay, and will be finished when the task is done..
*
*/
public class ConnectVpnActivity extends Activity implements VpnStateService.VpnStateListener {
private static final int PREPARE_VPN_SERVICE = 0;
public static final String KEY_REFRESH_DATA = "ConnectVpnActivity.refresh_data";
private VpnStateService mService;
private VpnProfile mProfile;
public interface VpnServiceIntentProvider {
public Intent getIntent();
}
private VpnServiceIntentProvider vpnServiceIntentProvider;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ((VpnStateService.LocalBinder)service).getService();
//we first need the service
if (Config.getInstance().getVpnState() == VpnStateService.State.CONNECTED) {
if (mService != null) {
mService.disconnect();
cleanUp(VpnStateService.State.DISABLED);
}
} else {
connect();
}
}
};
private void bindVpnService() {
Context context = getApplicationContext();
context.bindService(new Intent(context, VpnStateService.class),
mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
vpnServiceIntentProvider = new VpnServiceIntentProvider() {
@Override
public Intent getIntent() {
Intent intent = new Intent(ConnectVpnActivity.this, CharonVpnService.class);
intent.putExtra(Config.VPN_PROFILE_BUNDLE_KEY, mProfile);
return intent;
}
};
if (!Config.getInstance().getAuthToken().isEmpty()) {
bindVpnService();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
getApplicationContext().unbindService(mServiceConnection);
}
}
@Override
public void stateChanged() {
}
/**
* Prepare the VpnService. If this succeeds the current VPN profile is
* started.
*/
protected void prepareVpnService() {
Intent intent;
try {
intent = VpnService.prepare(this);
}
catch (IllegalStateException ex) {
/* this happens if the always-on VPN feature (Android 4.2+) is activated */
cleanUp(VpnStateService.State.DISABLED);
return;
}
if (intent != null) {
try {
startActivityForResult(intent, PREPARE_VPN_SERVICE);
}
catch (ActivityNotFoundException ex) {
/* it seems some devices, even though they come with Android 4,
* don't have the VPN components built into the system image.
* com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog
* will not be found then */
cleanUp(VpnStateService.State.DISABLED);
}
}
else {
/* user already granted permission to use VpnService */
onActivityResult(PREPARE_VPN_SERVICE, RESULT_OK, null);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case PREPARE_VPN_SERVICE:
if (resultCode == RESULT_OK && mProfile != null) {
startService(vpnServiceIntentProvider.getIntent());
cleanUp(VpnStateService.State.CONNECTING);
} else {
sendState(VpnStateService.State.DISABLED);
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
}
}
private void connect() {
sendState(VpnStateService.State.CONNECTING);
String token = Config.getInstance().getAuthToken();
BackendConnector backendConnector = new BackendConnector(this);
backendConnector.authenticateUser(Config.getInstance().getPhoneNumber(),
Config.getInstance().getDeviceId(), token,
new BackendConnector.ResponseCallback<PlumberAuthResponse>() {
@Override
public void onRequestComplete(int statusCode, final PlumberAuthResponse response) {
switch (statusCode) {
//Errors
case 0:
cleanUp(VpnStateService.State.DISABLED);
break;
case 404:
Config.getInstance().saveAuthToken("");
cleanUp(VpnStateService.State.DISABLED);
break;
case 200:
final Runnable vpnConnectRunnable = new Runnable() {
@Override
public void run() {
//save new auth token for next use!
Config.getInstance().saveAuthToken(response.getUserToken());
mProfile = VpnConfigurator.getVpnProfile(response.getUsername(),
response.getPassword(), response.getPsk());
prepareVpnService();
}
};
vpnConnectRunnable.run();
break;
default:
// this should never be reached
cleanUp(VpnStateService.State.DISABLED);
break;
}
}
}
);
}
private void sendState(VpnStateService.State state) {
Config.getInstance().setVpnState(state);
final Intent resultIntent = new Intent(Config.VPN_CONNECT);
resultIntent.putExtra(KEY_REFRESH_DATA, false);
sendBroadcast(resultIntent);
}
private void cleanUp(VpnStateService.State state){
sendState(state);
finish();
}
}