summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2018-12-07 16:50:41 +0100
committercyBerta <cyberta@riseup.net>2018-12-07 16:50:41 +0100
commit9b832c4386f0f839006df68dd75d98fc173344d0 (patch)
tree378cfcb2e03885f786f41ffb04aef0eaf0d971a3 /app/src/main
parent1f5095b59c93b04120a2e10f0b083b4f6846ca68 (diff)
first shot gatewayselection
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml1
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java21
-rw-r--r--app/src/main/java/org/spongycastle/util/encoders/Encoder.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java19
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Constants.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipFragment.java83
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipSetupListener.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java287
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/MainActivity.java127
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderObservable.java31
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/StartActivity.java154
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java58
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java15
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java24
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java24
-rw-r--r--app/src/main/res/drawable/cust_toast_background.xml19
-rwxr-xr-xapp/src/main/res/drawable/retry.pngbin0 -> 890 bytes
-rw-r--r--app/src/main/res/layout/custom_toast.xml24
-rw-r--r--app/src/main/res/values/dimens.xml2
-rw-r--r--app/src/main/res/values/untranslatable.xml4115
23 files changed, 785 insertions, 4254 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 66f36256..a6752cf7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
index 5fbb440b..e7576bb5 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
@@ -214,6 +214,7 @@ public class VpnStatus {
}
public interface StateListener {
+ String STATE_CONNECTRETRY = "CONNECTRETRY";
void updateState(String state, String logmessage, int localizedResId, ConnectionStatus level);
void setConnectedVPN(String uuid);
@@ -267,10 +268,12 @@ public class VpnStatus {
public synchronized static void addStateListener(StateListener sl) {
- if (!stateListener.contains(sl)) {
- stateListener.add(sl);
- if (mLaststate != null)
- sl.updateState(mLaststate, mLaststatemsg, mLastStateresid, mLastLevel);
+ synchronized (stateListener) {
+ if (!stateListener.contains(sl)) {
+ stateListener.add(sl);
+ if (mLaststate != null)
+ sl.updateState(mLaststate, mLaststatemsg, mLastStateresid, mLastLevel);
+ }
}
}
@@ -351,7 +354,9 @@ public class VpnStatus {
public synchronized static void removeStateListener(StateListener sl) {
- stateListener.remove(sl);
+ synchronized (stateListener) {
+ stateListener.remove(sl);
+ }
}
@@ -384,8 +389,10 @@ public class VpnStatus {
mLastLevel = level;
- for (StateListener sl : stateListener) {
- sl.updateState(state, msg, resid, level);
+ synchronized (stateListener) {
+ for (StateListener sl : stateListener) {
+ sl.updateState(state, msg, resid, level);
+ }
}
//newLogItem(new LogItem((LogLevel.DEBUG), String.format("New OpenVPN Status (%s->%s): %s",state,level.toString(),msg)));
}
diff --git a/app/src/main/java/org/spongycastle/util/encoders/Encoder.java b/app/src/main/java/org/spongycastle/util/encoders/Encoder.java
index 2007ac3e..a506c4f2 100644
--- a/app/src/main/java/org/spongycastle/util/encoders/Encoder.java
+++ b/app/src/main/java/org/spongycastle/util/encoders/Encoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014 Arne Schwabe
+ * Copyright (c) 2012-2016 Arne Schwabe
* Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
diff --git a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
index 15fd85f8..ce48e4c6 100644
--- a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
+++ b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
@@ -1,18 +1,33 @@
package se.leap.bitmaskclient;
import android.content.Context;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.support.multidex.MultiDexApplication;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT;
+import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences;
+
/**
* Created by cyberta on 24.10.17.
*/
public class BitmaskApp extends MultiDexApplication {
+ private final static String TAG = BitmaskApp.class.getSimpleName();
private RefWatcher refWatcher;
+ private ProviderObservable providerObservable;
+ private SharedPreferences preferences;
+
@Override
public void onCreate() {
@@ -25,6 +40,10 @@ public class BitmaskApp extends MultiDexApplication {
refWatcher = LeakCanary.install(this);
// Normal app init code...*/
PRNGFixes.apply();
+ preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ providerObservable = ProviderObservable.getInstance();
+ providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences));
+ EipSetupObserver.init(this, preferences);
}
/**
diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java
index 15bec955..b05fedf9 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java
@@ -51,6 +51,7 @@ public interface Constants {
String EIP_RESTART_ON_BOOT = "EIP.RESTART_ON_BOOT";
String EIP_IS_ALWAYS_ON = "EIP.EIP_IS_ALWAYS_ON";
String EIP_EARLY_ROUTES = "EIP.EARLY_ROUTES";
+ String EIP_N_CLOSEST_GATEWAY = "EIP.N_CLOSEST_GATEWAY";
//////////////////////////////////////////////
@@ -64,6 +65,7 @@ public interface Constants {
String PROVIDER_KEY = "Constants.PROVIDER_KEY";
String PROVIDER_CONFIGURED = "Constants.PROVIDER_CONFIGURED";
String PROVIDER_EIP_DEFINITION = "Constants.EIP_DEFINITION";
+ String PROVIDER_PROFILE_UUID = "Constants.PROVIDER_PROFILE_UUID";
//////////////////////////////////////////////
// CREDENTIAL CONSTANTS
@@ -84,6 +86,7 @@ public interface Constants {
String BROADCAST_EIP_EVENT = "BROADCAST.EIP_EVENT";
String BROADCAST_PROVIDER_API_EVENT = "BROADCAST.PROVIDER_API_EVENT";
+ String BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT = "BROADCAST.GATEWAY_SETUP_WATCHER_EVENT";
String BROADCAST_RESULT_CODE = "BROADCAST.RESULT_CODE";
String BROADCAST_RESULT_KEY = "BROADCAST.RESULT_KEY";
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index fb4f16c7..30959435 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -27,6 +27,7 @@ import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
@@ -35,10 +36,13 @@ import android.support.v7.app.AlertDialog;
import android.support.v7.widget.AppCompatImageView;
import android.support.v7.widget.AppCompatTextView;
import android.util.Log;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
import java.util.Observable;
import java.util.Observer;
@@ -58,17 +62,20 @@ import se.leap.bitmaskclient.views.VpnStateImage;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK;
-import static se.leap.bitmaskclient.Constants.DEFAULT_BITMASK;
import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;
import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_LOG_IN;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER;
import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.EipSetupObserver.connectionRetry;
+import static se.leap.bitmaskclient.EipSetupObserver.gatewayOrder;
+import static se.leap.bitmaskclient.EipSetupObserver.reconnectingWithDifferentGateway;
import static se.leap.bitmaskclient.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE;
import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message;
import static se.leap.bitmaskclient.utils.ConfigHelper.isDefaultBitmask;
+import static se.leap.bitmaskclient.utils.ViewHelper.convertDimensionToPx;
public class EipFragment extends Fragment implements Observer {
@@ -94,6 +101,8 @@ public class EipFragment extends Fragment implements Observer {
@InjectView(R.id.vpn_route)
AppCompatTextView vpnRoute;
+
+
private EipStatus eipStatus;
//---saved Instance -------
@@ -193,7 +202,6 @@ public class EipFragment extends Fragment implements Observer {
if (activity != null) {
getActivity().unbindService(openVpnConnection);
}
- Log.d(TAG, "broadcast unregistered");
}
@Override
@@ -289,7 +297,7 @@ public class EipFragment extends Fragment implements Observer {
Log.e(TAG, "context is null when trying to start VPN");
return;
}
- EipCommand.startVPN(context, false);
+ EipCommand.startVPN(context.getApplicationContext(), false);
vpnStateImage.showProgress();
routedText.setVisibility(GONE);
vpnRoute.setVisibility(GONE);
@@ -302,7 +310,7 @@ public class EipFragment extends Fragment implements Observer {
Log.e(TAG, "context is null when trying to stop EIP");
return;
}
- EipCommand.stopVPN(context);
+ EipCommand.stopVPN(context.getApplicationContext());
}
private void askPendingStartCancellation() {
@@ -378,6 +386,8 @@ public class EipFragment extends Fragment implements Observer {
} else {
Log.e("EipFragment", "activity is null");
}
+ } else if (observable instanceof ProviderObservable) {
+ provider = ((ProviderObservable) observable).getCurrentProvider();
}
}
@@ -388,13 +398,16 @@ public class EipFragment extends Fragment implements Observer {
return;
}
- if (eipStatus.isConnecting()) {
- mainButton.setText(activity.getString(android.R.string.cancel));
- vpnStateImage.setStateIcon(R.drawable.vpn_connecting);
- vpnStateImage.showProgress();
- routedText.setVisibility(GONE);
- vpnRoute.setVisibility(GONE);
- colorBackgroundALittle();
+ Log.d(TAG, "eip fragment eipStatus state: " + eipStatus.getState() + " - level: " + eipStatus.getLevel() + " - is reconnecting: " + eipStatus.isReconnecting());
+
+
+ if (eipStatus.isConnecting() ) {
+ showConnectingLayout(activity);
+ Log.d(TAG, "eip show connecting layout");
+ if (eipStatus.isReconnecting()) {
+ Log.d(TAG, "eip show reconnecting toast!");
+ showReconnectToast(activity);
+ }
} else if (eipStatus.isConnected() ) {
mainButton.setText(activity.getString(R.string.vpn_button_turn_off));
vpnStateImage.setStateIcon(R.drawable.vpn_connected);
@@ -413,7 +426,11 @@ public class EipFragment extends Fragment implements Observer {
vpnRoute.setVisibility(VISIBLE);
setVpnRouteText();
colorBackgroundALittle();
- } else {
+ } else if (eipStatus.isDisconnected() && reconnectingWithDifferentGateway()) {
+ showConnectingLayout(activity);
+ showRetryToast(activity);
+ }
+ else {
mainButton.setText(activity.getString(R.string.vpn_button_turn_on));
vpnStateImage.setStateIcon(R.drawable.vpn_disconnected);
vpnStateImage.stopProgress(false);
@@ -423,6 +440,48 @@ public class EipFragment extends Fragment implements Observer {
}
}
+ private void showToast(Activity activity, String message, boolean vibrateLong) {
+ LayoutInflater inflater = getLayoutInflater();
+ View layout = inflater.inflate(R.layout.custom_toast,
+ (ViewGroup) activity.findViewById(R.id.custom_toast_container));
+
+ TextView text = (TextView) layout.findViewById(R.id.text);
+ text.setText(message);
+
+ Vibrator v = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
+ if (vibrateLong) {
+ v.vibrate(100);
+ v.vibrate(200);
+ } else {
+ v.vibrate(100);
+ }
+
+ Toast toast = new Toast(activity.getApplicationContext());
+ toast.setGravity(Gravity.BOTTOM, 0, convertDimensionToPx(this.getContext(), R.dimen.stdpadding));
+ toast.setDuration(Toast.LENGTH_LONG);
+ toast.setView(layout);
+ toast.show();
+ }
+ private void showReconnectToast(Activity activity) {
+ String message = (String.format("Retry %d of %d before the next closest gateway will be selected.", connectionRetry()+1, 5));
+ showToast(activity, message, false);
+ }
+
+ private void showRetryToast(Activity activity) {
+ int nClosestGateway = gatewayOrder();
+ String message = String.format("Server number " + nClosestGateway + " not reachable. Trying next gateway.");
+ showToast(activity, message, true );
+ }
+
+ private void showConnectingLayout(Context activity) {
+ mainButton.setText(activity.getString(android.R.string.cancel));
+ vpnStateImage.setStateIcon(R.drawable.vpn_connecting);
+ vpnStateImage.showProgress();
+ routedText.setVisibility(GONE);
+ vpnRoute.setVisibility(GONE);
+ colorBackgroundALittle();
+ }
+
private boolean isOpenVpnRunningWithoutNetwork() {
boolean isRunning = false;
try {
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipSetupListener.java b/app/src/main/java/se/leap/bitmaskclient/EipSetupListener.java
new file mode 100644
index 00000000..71e2fd52
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/EipSetupListener.java
@@ -0,0 +1,12 @@
+package se.leap.bitmaskclient;
+
+import android.content.Intent;
+
+/**
+ * Created by cyberta on 05.12.18.
+ */
+public interface EipSetupListener {
+ void handleEipEvent(Intent intent);
+
+ void handleProviderApiEvent(Intent intent);
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java
new file mode 100644
index 00000000..8e6ae7a6
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java
@@ -0,0 +1,287 @@
+package se.leap.bitmaskclient;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+
+import java.util.Vector;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import de.blinkt.openvpn.LaunchVPN;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ConnectionStatus;
+import de.blinkt.openvpn.core.VpnStatus;
+import se.leap.bitmaskclient.eip.EipCommand;
+import se.leap.bitmaskclient.eip.EipStatus;
+import se.leap.bitmaskclient.eip.Gateway;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET;
+import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE;
+import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
+import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;
+import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN;
+import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP;
+import static se.leap.bitmaskclient.Constants.EIP_REQUEST;
+import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;
+import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE;
+import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences;
+
+/**
+ * Created by cyberta on 05.12.18.
+ */
+class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListener {
+
+ private static final String TAG = EipSetupObserver.class.getName();
+
+ //The real timout is 4*2s + 1*4s + 1*8s + 1*16s + 1*32s + 1*64s = 132 s;
+ private static final String TIMEOUT = "4";
+ private Context context;
+ private VpnProfile setupVpnProfile;
+ private String observedProfileFromVpnStatus;
+ AtomicBoolean changingGateway = new AtomicBoolean(false);
+ AtomicInteger setupNClosestGateway = new AtomicInteger();
+ AtomicInteger reconnectTry = new AtomicInteger();
+ private Vector<EipSetupListener> listeners = new Vector<>();
+ private SharedPreferences preferences;
+ private static EipSetupObserver instance;
+
+ private EipSetupObserver(Context context, SharedPreferences preferences) {
+ this.context = context;
+ this.preferences = preferences;
+ IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT);
+ updateIntentFilter.addAction(BROADCAST_EIP_EVENT);
+ updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT);
+ updateIntentFilter.addCategory(CATEGORY_DEFAULT);
+ LocalBroadcastManager.getInstance(context.getApplicationContext()).registerReceiver(this, updateIntentFilter);
+ Log.d(TAG, "broadcast registered");
+ instance = this;
+ }
+
+ public static void init(Context context, SharedPreferences preferences) {
+ if (instance == null) {
+ instance = new EipSetupObserver(context, preferences);
+ }
+ }
+
+ public static boolean reconnectingWithDifferentGateway() {
+ return instance.reconnectTry.get() > 0;
+ }
+
+ public static int connectionRetry() {
+ return instance.reconnectTry.get();
+ }
+
+ public static int gatewayOrder() {
+ return instance.setupNClosestGateway.get();
+ }
+
+ public static synchronized void addListener(EipSetupListener listener) {
+ if (instance.listeners.contains(listener)) {
+ return;
+ }
+ instance.listeners.add(listener);
+ }
+
+ public static synchronized void removeListener(EipSetupListener listener) {
+ instance.listeners.remove(listener);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "received Broadcast");
+
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
+ switch (action) {
+ case BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT:
+ handleGatewaySetupObserverEvent(intent);
+ break;
+ case BROADCAST_EIP_EVENT:
+ handleEipEvent(intent);
+ break;
+ case BROADCAST_PROVIDER_API_EVENT:
+ handleProviderApiEvent(intent);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void handleProviderApiEvent(Intent intent) {
+
+
+ int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
+ Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY);
+ if (resultData == null) {
+ resultData = Bundle.EMPTY;
+ }
+
+ Provider provider;
+ switch (resultCode) {
+ case CORRECTLY_DOWNLOADED_EIP_SERVICE:
+ provider = resultData.getParcelable(PROVIDER_KEY);
+ ProviderObservable.getInstance().updateProvider(provider);
+ PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ if (EipStatus.getInstance().isDisconnected()) {
+ EipCommand.startVPN(context.getApplicationContext(), true);
+ }
+ break;
+ case CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE:
+ provider = resultData.getParcelable(PROVIDER_KEY);
+ ProviderObservable.getInstance().updateProvider(provider);
+ EipCommand.startVPN(context.getApplicationContext(), true);
+ break;
+ default:
+ break;
+ }
+
+
+
+ for (EipSetupListener listener : listeners) {
+ listener.handleProviderApiEvent(intent);
+ }
+
+ }
+
+
+ private void handleEipEvent(Intent intent) {
+ int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
+ String eipRequest = intent.getStringExtra(EIP_REQUEST);
+ if (eipRequest == null) {
+ return;
+ }
+ switch (eipRequest) {
+ case EIP_ACTION_START:
+ case EIP_ACTION_START_ALWAYS_ON_VPN:
+ if (resultCode == RESULT_CANCELED) {
+ //setup failed
+ finishGatewaySetup(false);
+ }
+ break;
+ case EIP_ACTION_STOP:
+ //setup was manually cancelled
+ finishGatewaySetup(false);
+ break;
+ default:
+ break;
+ }
+
+ for (EipSetupListener listener : listeners) {
+ listener.handleEipEvent(intent);
+ }
+
+ }
+
+ private void handleGatewaySetupObserverEvent(Intent event) {
+ if (observedProfileFromVpnStatus != null || setupVpnProfile != null) {
+ //finish last setup observation
+ Log.d(TAG, "finish last gateway setup");
+ finishGatewaySetup(true);
+ }
+
+ VpnProfile vpnProfile = (VpnProfile) event.getSerializableExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE);
+ if (vpnProfile == null) {
+ Log.e(TAG, "Tried to setup non existing vpn profile.");
+ return;
+ }
+ setupVpnProfile = vpnProfile;
+ setupNClosestGateway.set(event.getIntExtra(Gateway.KEY_N_CLOSEST_GATEWAY, 0));
+ Log.d(TAG, "bitmaskapp add state listener");
+ VpnStatus.addStateListener(this);
+
+ launchVPN(setupVpnProfile);
+ }
+
+ private void launchVPN(VpnProfile vpnProfile) {
+ Intent intent = new Intent(context.getApplicationContext(), LaunchVPN.class);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
+ intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, vpnProfile);
+ intent.putExtra(Gateway.KEY_N_CLOSEST_GATEWAY, setupNClosestGateway.get());
+ context.startActivity(intent);
+ }
+
+ @Override
+ public void updateState(String state, String logmessage, int localizedResId, ConnectionStatus level) {
+ // VpnStatus.updateStateString("NOPROCESS", "No process running.", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED);
+
+ Log.d(TAG, "vpn status: " + state + " - " + logmessage + " - " + level);
+ if (observedProfileFromVpnStatus == null ||
+ setupVpnProfile == null) {
+ return;
+ }
+ if (!observedProfileFromVpnStatus.equals(setupVpnProfile.getUUIDString())) {
+ Log.d(TAG, "vpn profile to setup and observed profile currently is used differ: " + setupVpnProfile.getUUIDString() + " vs. " + observedProfileFromVpnStatus);
+ return;
+ }
+
+ Log.d(TAG, "trying gateway: " + setupVpnProfile.getName());
+
+ if ("CONNECTRETRY".equals(state) && LEVEL_CONNECTING_NO_SERVER_REPLY_YET.equals(level)) {
+ if (TIMEOUT.equals(logmessage)) {
+ Log.e(TAG, "Timeout reached! Try next gateway!");
+ selectNextGateway();
+ return;
+ }
+ int current = reconnectTry.get();
+ reconnectTry.set(current + 1);
+ } else if ("NOPROCESS".equals(state) && ConnectionStatus.LEVEL_NOTCONNECTED == level) {
+ //??
+ } else if ("CONNECTED".equals(state)) {
+ //saveLastProfile(context.getApplicationContext(), setupVpnProfile.getUUIDString());
+ if (setupNClosestGateway.get() > 0) {
+ //at least one failed gateway -> did the provider change it's gateways?
+ SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
+ Provider provider = getSavedProviderFromSharedPreferences(preferences);
+ ProviderAPICommand.execute(context, ProviderAPI.DOWNLOAD_SERVICE_JSON, provider);
+ }
+ finishGatewaySetup(false);
+ } else if ("TCP_CONNECT".equals(state)) {
+ changingGateway.set(false);
+ }
+ }
+
+
+ private void selectNextGateway() {
+ changingGateway.set(true);
+ reconnectTry.set(0);
+ EipCommand.startVPN(context.getApplicationContext(), false, setupNClosestGateway.get() + 1);
+ }
+
+ private void finishGatewaySetup(boolean changingGateway) {
+ VpnStatus.removeStateListener(this);
+ setupVpnProfile = null;
+ setupNClosestGateway.set(0);
+ observedProfileFromVpnStatus = null;
+ this.changingGateway.set(changingGateway);
+ this.reconnectTry.set(0);
+ }
+
+ /**
+ * gets called as soon as a new VPN is about to launch
+ *
+ * @param uuid
+ */
+ @Override
+ public void setConnectedVPN(String uuid) {
+ observedProfileFromVpnStatus = uuid;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
index 84c7c16a..30aa5baa 100644
--- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
@@ -17,16 +17,12 @@
package se.leap.bitmaskclient;
-import android.content.BroadcastReceiver;
-import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
-import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
@@ -35,18 +31,16 @@ import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
+import java.util.Observable;
+import java.util.Observer;
+
import se.leap.bitmaskclient.drawer.NavigationDrawerFragment;
import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.fragments.LogFragment;
-import se.leap.bitmaskclient.utils.ConfigHelper;
-import static android.content.Intent.CATEGORY_DEFAULT;
-import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT;
-import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP;
import static se.leap.bitmaskclient.Constants.EIP_REQUEST;
import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP;
@@ -54,26 +48,22 @@ import static se.leap.bitmaskclient.Constants.REQUEST_CODE_LOG_IN;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER;
import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
import static se.leap.bitmaskclient.EipFragment.ASK_TO_CANCEL_VPN;
-import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE;
-import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.ProviderAPI.ERRORS;
import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE;
import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE;
import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed;
import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message;
-import static se.leap.bitmaskclient.utils.PreferenceHelper.getSavedProviderFromSharedPreferences;
import static se.leap.bitmaskclient.utils.PreferenceHelper.storeProviderInPreferences;
-public class MainActivity extends AppCompatActivity {
+public class MainActivity extends AppCompatActivity implements EipSetupListener, Observer {
public final static String TAG = MainActivity.class.getSimpleName();
- private Provider provider = new Provider();
+ private Provider provider;
private SharedPreferences preferences;
private NavigationDrawerFragment navigationDrawerFragment;
- private MainActivityBroadcastReceiver mainActivityBroadcastReceiver;
public final static String ACTION_SHOW_VPN_FRAGMENT = "action_show_vpn_fragment";
public final static String ACTION_SHOW_LOG_FRAGMENT = "action_show_log_fragment";
@@ -88,14 +78,11 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.a_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
- mainActivityBroadcastReceiver = new MainActivityBroadcastReceiver();
- setUpBroadcastReceiver();
-
navigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
- provider = getSavedProviderFromSharedPreferences(preferences);
+ provider = ProviderObservable.getInstance().getCurrentProvider();
// Set up the drawer.
navigationDrawerFragment.setUp(
@@ -108,6 +95,7 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onResume() {
super.onResume();
+ EipSetupObserver.addListener(this);
}
@Override
@@ -183,17 +171,18 @@ public class MainActivity extends AppCompatActivity {
}
storeProviderInPreferences(preferences, provider);
+ ProviderObservable.getInstance().updateProvider(provider);
navigationDrawerFragment.refresh();
switch (requestCode) {
case REQUEST_CODE_SWITCH_PROVIDER:
- EipCommand.stopVPN(this);
+ EipCommand.stopVPN(this.getApplicationContext());
break;
case REQUEST_CODE_CONFIGURE_LEAP:
Log.d(TAG, "REQUEST_CODE_CONFIGURE_LEAP - onActivityResult - MainActivity");
break;
case REQUEST_CODE_LOG_IN:
- EipCommand.startVPN(this, true);
+ EipCommand.startVPN(this.getApplicationContext(), true);
break;
}
}
@@ -210,51 +199,16 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onPause() {
super.onPause();
+ EipSetupObserver.removeListener(this);
}
@Override
- protected void onDestroy() {
- LocalBroadcastManager.getInstance(this).unregisterReceiver(mainActivityBroadcastReceiver);
- mainActivityBroadcastReceiver = null;
- super.onDestroy();
- }
-
- private void setUpBroadcastReceiver() {
- IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_EIP_EVENT);
- updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT);
- updateIntentFilter.addCategory(CATEGORY_DEFAULT);
- LocalBroadcastManager.getInstance(this).registerReceiver(mainActivityBroadcastReceiver, updateIntentFilter);
- Log.d(TAG, "broadcast registered");
- }
-
- private class MainActivityBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "received Broadcast");
-
- String action = intent.getAction();
- if (action == null) {
- return;
- }
-
- int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
- Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY);
- if (resultData == null) {
- resultData = Bundle.EMPTY;
- }
-
- switch (action) {
- case BROADCAST_EIP_EVENT:
- handleEIPEvent(resultCode, resultData);
- break;
- case BROADCAST_PROVIDER_API_EVENT:
- handleProviderApiEvent(resultCode, resultData);
- break;
- }
+ public void handleEipEvent(Intent intent) {
+ int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
+ Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY);
+ if (resultData == null) {
+ resultData = Bundle.EMPTY;
}
- }
-
- private void handleEIPEvent(int resultCode, Bundle resultData) {
String request = resultData.getString(EIP_REQUEST);
if (request == null) {
@@ -263,46 +217,26 @@ public class MainActivity extends AppCompatActivity {
switch (request) {
case EIP_ACTION_START:
- switch (resultCode) {
- case RESULT_OK:
- break;
- case RESULT_CANCELED:
- String error = resultData.getString(ERRORS);
- if (LeapSRPSession.loggedIn() || provider.allowsAnonymous()) {
- showMainActivityErrorDialog(error);
- } else {
- askUserToLogIn(getString(vpn_certificate_user_message));
- }
- break;
- }
- break;
- case EIP_ACTION_STOP:
- switch (resultCode) {
- case RESULT_OK:
- break;
- case RESULT_CANCELED:
- break;
+ if (resultCode == RESULT_CANCELED) {
+ String error = resultData.getString(ERRORS);
+ if (LeapSRPSession.loggedIn() || provider.allowsAnonymous()) {
+ showMainActivityErrorDialog(error);
+ } else {
+ askUserToLogIn(getString(vpn_certificate_user_message));
+ }
}
break;
}
}
- public void handleProviderApiEvent(int resultCode, Bundle resultData) {
- // TODO call DOWNLOAD_EIP_SERVICES ore remove respective cases
+ @Override
+ public void handleProviderApiEvent(Intent intent) {
+ int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
+
switch (resultCode) {
- case CORRECTLY_DOWNLOADED_EIP_SERVICE:
- provider = resultData.getParcelable(PROVIDER_KEY);
- EipCommand.startVPN(this, true);
- break;
case INCORRECTLY_DOWNLOADED_EIP_SERVICE:
// TODO CATCH ME IF YOU CAN - WHAT DO WE WANT TO DO?
break;
-
- case CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE:
- provider = resultData.getParcelable(PROVIDER_KEY);
- storeProviderInPreferences(preferences, provider);
- EipCommand.startVPN(this, true);
- break;
case INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE:
if (LeapSRPSession.loggedIn() || provider.allowsAnonymous()) {
showMainActivityErrorDialog(getString(downloading_vpn_certificate_failed));
@@ -313,6 +247,13 @@ public class MainActivity extends AppCompatActivity {
}
}
+ @Override
+ public void update(Observable o, Object arg) {
+ if (o instanceof ProviderObservable) {
+ this.provider = ((ProviderObservable) o).getCurrentProvider();
+ }
+ }
+
/**
* Shows an error dialog
*/
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderObservable.java b/app/src/main/java/se/leap/bitmaskclient/ProviderObservable.java
new file mode 100644
index 00000000..776c0e92
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderObservable.java
@@ -0,0 +1,31 @@
+package se.leap.bitmaskclient;
+
+import java.util.Observable;
+
+import se.leap.bitmaskclient.utils.PreferenceHelper;
+
+/**
+ * Created by cyberta on 05.12.18.
+ */
+public class ProviderObservable extends Observable {
+ private static ProviderObservable instance;
+ private Provider currentProvider;
+
+ public static ProviderObservable getInstance() {
+ if (instance == null) {
+ instance = new ProviderObservable();
+ }
+ return instance;
+ }
+
+ public synchronized void updateProvider(Provider provider) {
+ instance.currentProvider = provider;
+ instance.setChanged();
+ instance.notifyObservers();
+ }
+
+ public Provider getCurrentProvider() {
+ return instance.currentProvider;
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
index 9360bca3..4226d70f 100644
--- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
@@ -25,16 +25,17 @@ import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.util.Log;
+import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import de.blinkt.openvpn.core.Preferences;
import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.userstatus.User;
-import se.leap.bitmaskclient.utils.ConfigHelper;
+import se.leap.bitmaskclient.utils.PreferenceHelper;
import static se.leap.bitmaskclient.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE;
-import static se.leap.bitmaskclient.Constants.DEFAULT_BITMASK;
import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;
import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION;
import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION;
@@ -99,6 +100,9 @@ public class StartActivity extends Activity{
VpnStatus.initLogCache(getApplicationContext().getCacheDir());
User.init(getString(R.string.default_username));
+ fakeSetup();
+
+
prepareEIP();
}
@@ -184,7 +188,7 @@ public class StartActivity extends Activity{
} else {
Log.d(TAG, "vpn provider is configured");
if (getIntent() != null && getIntent().getBooleanExtra(EIP_RESTART_ON_BOOT, false)) {
- EipCommand.startVPN(this, true);
+ EipCommand.startVPN(this.getApplicationContext(), true);
finish();
return;
}
@@ -213,7 +217,8 @@ public class StartActivity extends Activity{
if (resultCode == RESULT_OK && data != null && data.hasExtra(Provider.KEY)) {
Provider provider = data.getParcelableExtra(Provider.KEY);
storeProviderInPreferences(preferences, provider);
- EipCommand.startVPN(this, false);
+ ProviderObservable.getInstance().updateProvider(provider);
+ EipCommand.startVPN(this.getApplicationContext(), false);
showMainActivity();
} else if (resultCode == RESULT_CANCELED) {
finish();
@@ -229,4 +234,145 @@ public class StartActivity extends Activity{
finish();
}
+ private void fakeSetup() {
+ PreferenceHelper.putString(this, "Constants.EIP_DEFINITION.riseup.net", getRiseupEipJson());
+ PreferenceHelper.putString(this, "Constants.EIP_DEFINITION", getRiseupEipJson());
+ SharedPreferences prefs = Preferences.getDefaultSharedPreferences(this);
+ SharedPreferences.Editor prefsedit = prefs.edit();
+ prefsedit.remove("lastConnectedProfile").commit();
+ File f = new File(this.getCacheDir().getAbsolutePath() + "/android.conf");
+ if (f.exists()) {
+ Log.d(TAG, "android.conf exists -> delete:" + f.delete());
+ }
+
+ File filesDirectory = new File(this.getFilesDir().getAbsolutePath());
+ if (filesDirectory.exists() && filesDirectory.isDirectory()) {
+ File[] filesInDirectory = filesDirectory.listFiles();
+ for (File file : filesInDirectory) {
+ Log.d(TAG, "delete profile: " + file.getName() + ": "+ file.delete());
+
+ }
+ } else Log.d(TAG, "file folder doesn't exist");
+
+
+ Log.d(TAG, "faked eipjson: " + PreferenceHelper.getString(this, "Constants.EIP_DEFINITION", ""));
+ Log.d(TAG, "lastConnectedProfile is emty: " + (prefs.getString("lastConnectedProfile", null) == null));
+ }
+
+ private String getRiseupEipJson() {
+ return "{\n" +
+ " \"gateways\":[\n" +
+ " {\n" +
+ " \"capabilities\":{\n" +
+ " \"adblock\":false,\n" +
+ " \"filter_dns\":false,\n" +
+ " \"limited\":false,\n" +
+ " \"ports\":[\n" +
+ " \"443\"\n" +
+ " ],\n" +
+ " \"protocols\":[\n" +
+ " \"tcp\"\n" +
+ " ],\n" +
+ " \"transport\":[\n" +
+ " \"openvpn\"\n" +
+ " ],\n" +
+ " \"user_ips\":false\n" +
+ " },\n" +
+ " \"host\":\"garza.riseup.net\",\n" +
+ " \"ip_address\":\"198.252.153.28\",\n" +
+ " \"location\":\"seattle\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"capabilities\":{\n" +
+ " \"adblock\":false,\n" +
+ " \"filter_dns\":false,\n" +
+ " \"limited\":false,\n" +
+ " \"ports\":[\n" +
+ " \"443\"\n" +
+ " ],\n" +
+ " \"protocols\":[\n" +
+ " \"tcp\"\n" +
+ " ],\n" +
+ " \"transport\":[\n" +
+ " \"openvpn\"\n" +
+ " ],\n" +
+ " \"user_ips\":false\n" +
+ " },\n" +
+ " \"host\":\"no.giraffe.riseup.net\",\n" +
+ " \"ip_address\":\"37.218.242.212\",\n" +
+ " \"location\":\"amsterdam\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"capabilities\":{\n" +
+ " \"adblock\":false,\n" +
+ " \"filter_dns\":false,\n" +
+ " \"limited\":false,\n" +
+ " \"ports\":[\n" +
+ " \"443\"\n" +
+ " ],\n" +
+ " \"protocols\":[\n" +
+ " \"tcp\"\n" +
+ " ],\n" +
+ " \"transport\":[\n" +
+ " \"openvpn\"\n" +
+ " ],\n" +
+ " \"user_ips\":false\n" +
+ " },\n" +
+ " \"host\":\"no.tenca.riseup.net\",\n" +
+ " \"ip_address\":\"5.79.86.181\",\n" +
+ " \"location\":\"amsterdam\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"capabilities\":{\n" +
+ " \"adblock\":false,\n" +
+ " \"filter_dns\":false,\n" +
+ " \"limited\":false,\n" +
+ " \"ports\":[\n" +
+ " \"443\"\n" +
+ " ],\n" +
+ " \"protocols\":[\n" +
+ " \"tcp\"\n" +
+ " ],\n" +
+ " \"transport\":[\n" +
+ " \"openvpn\"\n" +
+ " ],\n" +
+ " \"user_ips\":false\n" +
+ " },\n" +
+ " \"host\":\"yal.riseup.net\",\n" +
+ " \"ip_address\":\"199.58.81.145\",\n" +
+ " \"location\":\"montreal\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"locations\":{\n" +
+ " \"amsterdam\":{\n" +
+ " \"country_code\":\"NL\",\n" +
+ " \"hemisphere\":\"N\",\n" +
+ " \"name\":\"Amsterdam\",\n" +
+ " \"timezone\":\"+2\"\n" +
+ " },\n" +
+ " \"montreal\":{\n" +
+ " \"country_code\":\"CA\",\n" +
+ " \"hemisphere\":\"N\",\n" +
+ " \"name\":\"Montreal\",\n" +
+ " \"timezone\":\"-5\"\n" +
+ " },\n" +
+ " \"seattle\":{\n" +
+ " \"country_code\":\"US\",\n" +
+ " \"hemisphere\":\"N\",\n" +
+ " \"name\":\"Seattle\",\n" +
+ " \"timezone\":\"-7\"\n" +
+ " }\n" +
+ " },\n" +
+ " \"openvpn_configuration\":{\n" +
+ " \"auth\":\"SHA1\",\n" +
+ " \"cipher\":\"AES-128-CBC\",\n" +
+ " \"keepalive\":\"10 30\",\n" +
+ " \"tls-cipher\":\"DHE-RSA-AES128-SHA\",\n" +
+ " \"tun-ipv6\":true\n" +
+ " },\n" +
+ " \"serial\":1,\n" +
+ " \"version\":1\n" +
+ "}";
+ }
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 971d973f..0e6d9b95 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -53,7 +53,9 @@ import se.leap.bitmaskclient.OnBootReceiver;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static android.content.Intent.CATEGORY_DEFAULT;
+import static de.blinkt.openvpn.LaunchVPN.EXTRA_TEMP_VPN_PROFILE;
import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT;
+import static se.leap.bitmaskclient.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE;
import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;
@@ -63,6 +65,7 @@ import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN;
import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES;
+import static se.leap.bitmaskclient.Constants.EIP_N_CLOSEST_GATEWAY;
import static se.leap.bitmaskclient.Constants.EIP_RECEIVER;
import static se.leap.bitmaskclient.Constants.EIP_REQUEST;
import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;
@@ -150,10 +153,12 @@ public final class EIP extends JobIntentService implements Observer {
if (intent.getParcelableExtra(EIP_RECEIVER) != null) {
mResultRef = new WeakReference<>((ResultReceiver) intent.getParcelableExtra(EIP_RECEIVER));
}
+ int nClosestGateway;
switch (action) {
case EIP_ACTION_START:
boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true);
- startEIP(earlyRoutes);
+ nClosestGateway = intent.getIntExtra(EIP_N_CLOSEST_GATEWAY, 0);
+ startEIP(earlyRoutes, nClosestGateway);
break;
case EIP_ACTION_START_ALWAYS_ON_VPN:
startEIPAlwaysOnVpn();
@@ -170,13 +175,17 @@ public final class EIP extends JobIntentService implements Observer {
}
}
+
/**
* Initiates an EIP connection by selecting a gateway and preparing and sending an
* Intent to {@link de.blinkt.openvpn.LaunchVPN}.
* It also sets up early routes.
+ * @param earlyRoutes if true, a void vpn gets set up
+ * @param nClosestGateway the gateway that is the n nearest one to the users place
*/
@SuppressLint("ApplySharedPref")
- private void startEIP(boolean earlyRoutes) {
+ private void startEIP(boolean earlyRoutes, int nClosestGateway) {
+ Log.d(TAG, "start EIP with early routes: " + earlyRoutes + " and nClosest Gateway: " + nClosestGateway);
if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) {
earlyRoutes();
}
@@ -186,16 +195,17 @@ public final class EIP extends JobIntentService implements Observer {
preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit();
}
- GatewaysManager gatewaysManager = gatewaysFromPreferences();
if (!isVPNCertificateValid()) {
setErrorResult(result, vpn_certificate_is_invalid, ERROR_INVALID_VPN_CERTIFICATE.toString());
- tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED);
+ tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED, result);
return;
}
- Gateway gateway = gatewaysManager.select();
+ GatewaysManager gatewaysManager = gatewaysFromPreferences();
+ Gateway gateway = gatewaysManager.select(nClosestGateway);
+
if (gateway != null && gateway.getProfile() != null) {
- launchActiveGateway(gateway);
+ launchActiveGateway(gateway, nClosestGateway);
tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_OK);
} else
tellToReceiverOrBroadcast(EIP_ACTION_START, RESULT_CANCELED);
@@ -209,11 +219,11 @@ public final class EIP extends JobIntentService implements Observer {
Log.d(TAG, "startEIPAlwaysOnVpn vpn");
GatewaysManager gatewaysManager = gatewaysFromPreferences();
- Gateway gateway = gatewaysManager.select();
+ Gateway gateway = gatewaysManager.select(0);
if (gateway != null && gateway.getProfile() != null) {
- Log.d(TAG, "startEIPAlwaysOnVpn eip launch avtive gateway vpn");
- launchActiveGateway(gateway);
+ Log.d(TAG, "startEIPAlwaysOnVpn eip launch closest gateway.");
+ launchActiveGateway(gateway, 0);
} else {
Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!");
}
@@ -234,13 +244,21 @@ public final class EIP extends JobIntentService implements Observer {
*
* @param gateway to connect to
*/
- private void launchActiveGateway(Gateway gateway) {
- Intent intent = new Intent(this, LaunchVPN.class);
- intent.setAction(Intent.ACTION_MAIN);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
- intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, gateway.getProfile());
- startActivity(intent);
+ private void launchActiveGateway(@NonNull Gateway gateway, int nClosestGateway) {
+ /*Intent gatewaySetupWatcherIntent = new Intent(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT);
+ gatewaySetupWatcherIntent.putExtra(EIP_REQUEST, )
+ gatewaySetupWatcherIntent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, gateway.getProfile());*/
+
+
+ Intent intent = new Intent(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT); //new Intent(this, LaunchVPN.class);
+ //intent.setAction(Intent.ACTION_MAIN);
+ //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ //intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
+ intent.putExtra(EXTRA_TEMP_VPN_PROFILE, gateway.getProfile());
+ intent.putExtra(Gateway.KEY_N_CLOSEST_GATEWAY, nClosestGateway);
+ //startActivity(intent);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+
}
/**
@@ -277,6 +295,14 @@ public final class EIP extends JobIntentService implements Observer {
}
/**
+ * Updates the eip.json. It containes information about the vpn service of a provider such as
+ * available gateways, supported protocols and open ports.
+ */
+ private void updateEipJson() {
+
+ }
+
+ /**
* read VPN certificate from preferences and check it
* broadcast result
*/
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
index 19735483..2bca5c9b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
@@ -10,10 +10,13 @@ import android.support.annotation.VisibleForTesting;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import se.leap.bitmaskclient.Provider;
+
import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP;
import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES;
+import static se.leap.bitmaskclient.Constants.EIP_N_CLOSEST_GATEWAY;
import static se.leap.bitmaskclient.Constants.EIP_RECEIVER;
/**
@@ -22,7 +25,7 @@ import static se.leap.bitmaskclient.Constants.EIP_RECEIVER;
public class EipCommand {
- public static void execute(@NotNull Context context, @NotNull String action) {
+ private static void execute(@NotNull Context context, @NotNull String action) {
execute(context, action, null, null);
}
@@ -33,7 +36,7 @@ public class EipCommand {
* filter for the EIP class
* @param resultReceiver The resultreceiver to reply to
*/
- public static void execute(@NotNull Context context, @NotNull String action, @Nullable ResultReceiver resultReceiver, @Nullable Intent vpnIntent) {
+ private static void execute(@NotNull Context context, @NotNull String action, @Nullable ResultReceiver resultReceiver, @Nullable Intent vpnIntent) {
// TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
if (vpnIntent == null) {
vpnIntent = new Intent();
@@ -48,6 +51,14 @@ public class EipCommand {
public static void startVPN(@NonNull Context context, boolean earlyRoutes) {
Intent baseIntent = new Intent();
baseIntent.putExtra(EIP_EARLY_ROUTES, earlyRoutes);
+ baseIntent.putExtra(EIP_N_CLOSEST_GATEWAY, 0);
+ execute(context, EIP_ACTION_START, null, baseIntent);
+ }
+
+ public static void startVPN(@NonNull Context context, boolean earlyRoutes, int nClosestGateway) {
+ Intent baseIntent = new Intent();
+ baseIntent.putExtra(EIP_EARLY_ROUTES, earlyRoutes);
+ baseIntent.putExtra(EIP_N_CLOSEST_GATEWAY, nClosestGateway);
execute(context, EIP_ACTION_START, null, baseIntent);
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
index df252500..861f5fd3 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
@@ -19,6 +19,7 @@ package se.leap.bitmaskclient.eip;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.VisibleForTesting;
+import android.util.Log;
import java.util.Observable;
@@ -26,6 +27,7 @@ import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.LogItem;
import de.blinkt.openvpn.core.ProfileManager;
import de.blinkt.openvpn.core.VpnStatus;
+import se.leap.bitmaskclient.Provider;
/**
* EipStatus is a Singleton that represents a reduced set of a vpn's ConnectionStatus.
@@ -76,7 +78,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
currentStatus.setLocalizedResId(localizedResId);
currentStatus.setLevel(level);
currentStatus.setEipLevel(level);
- if (tmp != currentStatus.getLevel()) {
+ if (tmp != currentStatus.getLevel() || "RECONNECTING".equals(state)) {
currentStatus.setChanged();
currentStatus.notifyObservers();
}
@@ -86,6 +88,10 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
public void setConnectedVPN(String uuid) {
}
+ public boolean isReconnecting() {
+ Log.d(TAG, "eip currentVPNStatus : " + currentStatus.getState() );
+ return "RECONNECTING".equals(currentStatus.getState());
+ }
private void setEipLevel(ConnectionStatus level) {
switch (level) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index 6cccdcd2..317a91bd 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -38,6 +38,7 @@ import de.blinkt.openvpn.core.ConfigParser;
public class Gateway {
public final static String TAG = Gateway.class.getSimpleName();
+ public final static String KEY_N_CLOSEST_GATEWAY = "N_CLOSEST_GATEWAY";
private JSONObject generalConfiguration;
private JSONObject secrets;
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
index 90c8f890..078e3fd5 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
@@ -1,20 +1,42 @@
package se.leap.bitmaskclient.eip;
+import android.util.Log;
+
import java.util.*;
public class GatewaySelector {
+ private final static String TAG = GatewaySelector.class.getSimpleName();
List<Gateway> gateways;
+ TreeMap<Integer, Set<Gateway>> offsets;
public GatewaySelector(List<Gateway> gateways) {
this.gateways = gateways;
+ this.offsets = calculateOffsets();
+
}
public Gateway select() {
return closestGateway();
}
+ public Gateway select(int nClosest) throws IndexOutOfBoundsException{
+ int i = 0;
+ for (Map.Entry<Integer,Set<Gateway>> entrySet : offsets.entrySet()) {
+ Iterator<Gateway> iterator = entrySet.getValue().iterator();
+ while (iterator.hasNext()) {
+ Gateway gateway = iterator.next();
+ if (i == nClosest) {
+ return gateway;
+ }
+ i = i+1;
+ }
+ }
+
+ Log.e(TAG, "There are less than " + nClosest + " Gateways available.");
+ return null;
+ }
+
private Gateway closestGateway() {
- TreeMap<Integer, Set<Gateway>> offsets = calculateOffsets();
return offsets.isEmpty() ? null : offsets.firstEntry().getValue().iterator().next();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
index 5b4db5af..003cef7d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -18,6 +18,7 @@ package se.leap.bitmaskclient.eip;
import android.content.Context;
import android.content.SharedPreferences;
+import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@@ -46,6 +47,8 @@ import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
*/
public class GatewaysManager {
+ private static final String TAG = GatewaysManager.class.getSimpleName();
+
private Context context;
private SharedPreferences preferences;
private List<Gateway> gateways = new ArrayList<>();
@@ -60,11 +63,11 @@ public class GatewaysManager {
/**
* select closest Gateway
- * @return the closest Gateway
+ * @return the n closest Gateway
*/
- public Gateway select() {
+ public Gateway select(int nClosest) {
GatewaySelector gatewaySelector = new GatewaySelector(gateways);
- return gatewaySelector.select();
+ return gatewaySelector.select(nClosest);
}
/**
diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
index b1da1284..11582d87 100644
--- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
@@ -31,6 +31,7 @@ import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION;
import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED;
import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION;
import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY;
+import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE_UUID;
import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
@@ -239,6 +240,29 @@ public class PreferenceHelper {
return result;
}
+ /*public static void saveLastProfile(Context context, String uuid) {
+ if (context == null) {
+ return;
+ }
+ putString(context, PROVIDER_PROFILE_UUID, uuid);
+ }
+
+ public static void clearLastProfile(Context context) {
+ if (context == null) {
+ return;
+ }
+ SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
+ preferences.edit().remove(PROVIDER_PROFILE_UUID).apply();
+ }
+
+ public static String getLastProfile(Context context){
+ return getString(context, PROVIDER_PROFILE_UUID, null);
+ }
+
+ public static void saveLastGatewayNumber(Context context, int number) {
+
+ }
+*/
public static String getString(Context context, String key, String defValue) {
SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
return preferences.getString(key, defValue);
diff --git a/app/src/main/res/drawable/cust_toast_background.xml b/app/src/main/res/drawable/cust_toast_background.xml
new file mode 100644
index 00000000..a27c96f8
--- /dev/null
+++ b/app/src/main/res/drawable/cust_toast_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<layer-list>
+ <item>
+ <shape android:shape="rectangle"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="50dp" />
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="50dp" />
+ <padding android:left="8dp" android:right="8dp"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:width="2dp" android:color="@color/colorPrimary"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/app/src/main/res/drawable/retry.png b/app/src/main/res/drawable/retry.png
new file mode 100755
index 00000000..ce722398
--- /dev/null
+++ b/app/src/main/res/drawable/retry.png
Binary files differ
diff --git a/app/src/main/res/layout/custom_toast.xml b/app/src/main/res/layout/custom_toast.xml
new file mode 100644
index 00000000..70a9df8c
--- /dev/null
+++ b/app/src/main/res/layout/custom_toast.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/custom_toast_container"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="16dp"
+ android:background="@drawable/cust_toast_background">
+ <ImageView android:src="@drawable/retry"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginRight="8dp"
+ android:layout_gravity="center"
+ />
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#000000"
+ android:layout_gravity="center"
+ tools:text="Could not connect to gateway XYZ (161.161.161.161). Trying different Gateway."
+ />
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 10c32471..338ad4d5 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -39,4 +39,6 @@
<dimen name="constraint_bottom_std">0.85</dimen>
<dimen name="constraint_top_compact">0.1</dimen>
<dimen name="constraint_bottom_compact">0.9</dimen>
+
+ <dimen name="toast_bottom_padding">20dp</dimen>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/untranslatable.xml b/app/src/main/res/values/untranslatable.xml
index 2e91366a..f1cc82ad 100644
--- a/app/src/main/res/values/untranslatable.xml
+++ b/app/src/main/res/values/untranslatable.xml
@@ -36,4118 +36,5 @@
<string name="state_user_vpn_password" translatable="false">Waiting for user VPN password</string>
<string name="state_user_vpn_password_cancelled" translatable="false">VPN password input dialog cancelled</string>
<string name="state_user_vpn_permission_cancelled" translatable="false">VPN API permission dialog cancelled</string>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <string name="crash_toast_text">OpenVPN for Android crashed, crash reported</string>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ <string name="gateway_not_reachable">Could not connect to gateway %s. Trying next one.</string>
</resources> \ No newline at end of file