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(); } }