summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
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.java12
-rw-r--r--app/src/main/java/org/spongycastle/util/encoders/Encoder.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java11
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Constants.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipFragment.java135
-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.java129
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java18
-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/VpnNotificationManager.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java77
-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.java32
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java30
-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/attrs.xml6
-rw-r--r--app/src/main/res/values/dimens.xml2
-rw-r--r--app/src/main/res/values/themes.xml4
-rw-r--r--app/src/main/res/values/untranslatable.xml4115
30 files changed, 824 insertions, 4385 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..0fae6183 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
@@ -15,12 +15,10 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.LinkedList;
import java.util.Locale;
-import java.util.Queue;
import java.util.Vector;
-import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CopyOnWriteArrayList;
import se.leap.bitmaskclient.R;
-import de.blinkt.openvpn.VpnProfile;
public class VpnStatus {
@@ -28,7 +26,7 @@ public class VpnStatus {
private static final LinkedList<LogItem> logbuffer;
private static Vector<LogListener> logListener;
- private static Vector<StateListener> stateListener;
+ private static CopyOnWriteArrayList<StateListener> stateListener;
private static Vector<ByteCountListener> byteCountListener;
private static String mLaststatemsg = "";
@@ -200,7 +198,7 @@ public class VpnStatus {
static {
logbuffer = new LinkedList<>();
logListener = new Vector<>();
- stateListener = new Vector<>();
+ stateListener = new CopyOnWriteArrayList<>();
byteCountListener = new Vector<>();
trafficHistory = new TrafficHistory();
@@ -214,6 +212,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);
@@ -270,7 +269,7 @@ public class VpnStatus {
if (!stateListener.contains(sl)) {
stateListener.add(sl);
if (mLaststate != null)
- sl.updateState(mLaststate, mLaststatemsg, mLastStateresid, mLastLevel);
+ sl.updateState(mLaststate, mLaststatemsg, mLastStateresid, mLastLevel);
}
}
@@ -383,7 +382,6 @@ public class VpnStatus {
mLastStateresid = resid;
mLastLevel = level;
-
for (StateListener sl : stateListener) {
sl.updateState(state, msg, resid, level);
}
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..fd9aa029 100644
--- a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
+++ b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
@@ -1,18 +1,25 @@
package se.leap.bitmaskclient;
import android.content.Context;
+import android.content.SharedPreferences;
import android.support.multidex.MultiDexApplication;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
+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;
+
@Override
public void onCreate() {
@@ -25,6 +32,10 @@ public class BitmaskApp extends MultiDexApplication {
refWatcher = LeakCanary.install(this);
// Normal app init code...*/
PRNGFixes.apply();
+ SharedPreferences 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..f9ee8fcf 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java
@@ -33,6 +33,8 @@ public interface Constants {
String DEFAULT_BITMASK = "normal";
String CUSTOM_BITMASK = "custom";
+ String ASK_TO_CANCEL_VPN = "ask_to_cancel_vpn";
+
//////////////////////////////////////////////
// EIP CONSTANTS
@@ -51,6 +53,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 +67,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 +88,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..a535b0cb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -19,7 +19,6 @@ package se.leap.bitmaskclient;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
@@ -27,6 +26,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 +35,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,23 +61,26 @@ 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.ASK_TO_CANCEL_VPN;
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 {
public final static String TAG = EipFragment.class.getSimpleName();
- public static final String ASK_TO_CANCEL_VPN = "ask_to_cancel_vpn";
private SharedPreferences preferences;
private Provider provider;
@@ -94,11 +100,13 @@ public class EipFragment extends Fragment implements Observer {
@InjectView(R.id.vpn_route)
AppCompatTextView vpnRoute;
+
+
private EipStatus eipStatus;
//---saved Instance -------
- private final static String KEY_SHOW_PENDING_START_CANCELLATION = "KEY_SHOW_PENDING_START_CANCELLATION";
- private final static String KEY_SHOW_ASK_TO_STOP_EIP = "KEY_SHOW_ASK_TO_STOP_EIP";
+ private final String KEY_SHOW_PENDING_START_CANCELLATION = "KEY_SHOW_PENDING_START_CANCELLATION";
+ private final String KEY_SHOW_ASK_TO_STOP_EIP = "KEY_SHOW_ASK_TO_STOP_EIP";
private boolean showPendingStartCancellation = false;
private boolean showAskToStopEip = false;
//------------------------
@@ -193,7 +201,6 @@ public class EipFragment extends Fragment implements Observer {
if (activity != null) {
getActivity().unbindService(openVpnConnection);
}
- Log.d(TAG, "broadcast unregistered");
}
@Override
@@ -289,7 +296,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 +309,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() {
@@ -316,22 +323,9 @@ public class EipFragment extends Fragment implements Observer {
showPendingStartCancellation = true;
alertDialog = alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title))
.setMessage(activity.getString(R.string.eip_cancel_connect_text))
- .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- stopEipIfPossible();
- }
- })
- .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- }
- }).setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- showPendingStartCancellation = false;
- }
- }).show();
+ .setPositiveButton((android.R.string.yes), (dialog, which) -> stopEipIfPossible())
+ .setNegativeButton(activity.getString(android.R.string.no), (dialog, which) -> {
+ }).setOnDismissListener(dialog -> showPendingStartCancellation = false).show();
}
@@ -345,22 +339,9 @@ public class EipFragment extends Fragment implements Observer {
showAskToStopEip = true;
alertDialog = alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title))
.setMessage(activity.getString(R.string.eip_warning_browser_inconsistency))
- .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- stopEipIfPossible();
- }
- })
- .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- }
- }).setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- showAskToStopEip = false;
- }
- }).show();
+ .setPositiveButton((android.R.string.yes), (dialog, which) -> stopEipIfPossible())
+ .setNegativeButton(activity.getString(android.R.string.no), (dialog, which) -> {
+ }).setOnDismissListener(dialog -> showAskToStopEip = false).show();
}
@Override
@@ -369,15 +350,12 @@ public class EipFragment extends Fragment implements Observer {
eipStatus = (EipStatus) observable;
Activity activity = getActivity();
if (activity != null) {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- handleNewState();
- }
- });
+ activity.runOnUiThread(() -> handleNewState());
} else {
Log.e("EipFragment", "activity is null");
}
+ } else if (observable instanceof ProviderObservable) {
+ provider = ((ProviderObservable) observable).getCurrentProvider();
}
}
@@ -388,13 +366,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 +394,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 +408,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..f17fe28e
--- /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);
+ instance = this;
+ }
+
+ public static void init(Context context, SharedPreferences preferences) {
+ if (instance == null) {
+ instance = new EipSetupObserver(context, preferences);
+ }
+ }
+
+ public static boolean reconnectingWithDifferentGateway() {
+ return instance.setupNClosestGateway.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!");
+ VpnStatus.logError("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..fd0c27d6 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,45 +31,39 @@ 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.ASK_TO_CANCEL_VPN;
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;
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/ProviderCredentialsBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java
index 64a17f41..961957d2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java
@@ -32,7 +32,6 @@ import android.support.v7.widget.AppCompatButton;
import android.support.v7.widget.AppCompatTextView;
import android.text.Editable;
import android.text.Html;
-import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
@@ -47,7 +46,6 @@ import org.json.JSONException;
import butterknife.InjectView;
import butterknife.OnClick;
import se.leap.bitmaskclient.Constants.CREDENTIAL_ERRORS;
-import se.leap.bitmaskclient.eip.EipCommand;
import se.leap.bitmaskclient.userstatus.User;
import static android.text.TextUtils.isEmpty;
@@ -60,9 +58,7 @@ import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
import static se.leap.bitmaskclient.Constants.CREDENTIALS_PASSWORD;
import static se.leap.bitmaskclient.Constants.CREDENTIALS_USERNAME;
import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;
-import static se.leap.bitmaskclient.ProviderAPI.BACKEND_ERROR_KEY;
import static se.leap.bitmaskclient.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE;
-import static se.leap.bitmaskclient.ProviderAPI.ERRORS;
import static se.leap.bitmaskclient.ProviderAPI.LOG_IN;
import static se.leap.bitmaskclient.ProviderAPI.SIGN_UP;
import static se.leap.bitmaskclient.ProviderAPI.USER_MESSAGE;
@@ -442,6 +438,7 @@ public abstract class ProviderCredentialsBaseActivity extends ConfigWizardBaseAc
finish();
}
+ //TODO: replace with EipSetupObserver
public class ProviderAPIBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java
index a29d4b61..c8070e18 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListBaseActivity.java
@@ -18,22 +18,12 @@
package se.leap.bitmaskclient;
import android.content.Intent;
-import android.content.IntentFilter;
import android.os.Bundle;
-import android.os.Handler;
import android.support.annotation.NonNull;
-import android.support.v4.app.DialogFragment;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.content.LocalBroadcastManager;
-import android.util.Log;
import android.widget.ListView;
import com.pedrogomez.renderers.Renderer;
-import org.jetbrains.annotations.NotNull;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@@ -45,17 +35,9 @@ import butterknife.InjectView;
import butterknife.OnItemClick;
import se.leap.bitmaskclient.fragments.AboutFragment;
-import static se.leap.bitmaskclient.Constants.BROADCAST_PROVIDER_API_EVENT;
-import static se.leap.bitmaskclient.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_ADD_PROVIDER;
import static se.leap.bitmaskclient.Constants.REQUEST_CODE_CONFIGURE_LEAP;
-import static se.leap.bitmaskclient.ProviderAPI.ERRORS;
-import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP;
-import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROVIDER_DETAILS;
-import static se.leap.bitmaskclient.ProviderSetupInterface.ProviderConfigState.PENDING_SHOW_FAILED_DIALOG;
-import static se.leap.bitmaskclient.ProviderSetupInterface.ProviderConfigState.PROVIDER_NOT_SET;
import static se.leap.bitmaskclient.ProviderSetupInterface.ProviderConfigState.SETTING_UP_PROVIDER;
-import static se.leap.bitmaskclient.ProviderSetupInterface.ProviderConfigState.SHOWING_PROVIDER_DETAILS;
import static se.leap.bitmaskclient.ProviderSetupInterface.ProviderConfigState.SHOW_FAILED_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/VpnNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/VpnNotificationManager.java
index 503ba536..566b3453 100644
--- a/app/src/main/java/se/leap/bitmaskclient/VpnNotificationManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/VpnNotificationManager.java
@@ -44,7 +44,7 @@ import static android.text.TextUtils.isEmpty;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT;
import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN;
-import static se.leap.bitmaskclient.EipFragment.ASK_TO_CANCEL_VPN;
+import static se.leap.bitmaskclient.Constants.ASK_TO_CANCEL_VPN;
import static se.leap.bitmaskclient.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
/**
diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
index 769bd887..a01a79ea 100644
--- a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
@@ -47,7 +47,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
-import android.widget.CompoundButton;
import android.widget.ListView;
import se.leap.bitmaskclient.DrawerSettingsAdapter;
@@ -201,12 +200,7 @@ public class NavigationDrawerFragment extends Fragment {
}
// Defer code dependent on restoration of previous instance state.
- this.drawerLayout.post(new Runnable() {
- @Override
- public void run() {
- drawerToggle.syncState();
- }
- });
+ this.drawerLayout.post(() -> drawerToggle.syncState());
this.drawerLayout.addDrawerListener(drawerToggle);
}
@@ -261,12 +255,7 @@ public class NavigationDrawerFragment extends Fragment {
private void setupSettingsListView() {
ListView drawerSettingsListView = drawerView.findViewById(R.id.settingsList);
- drawerSettingsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- selectItem(parent, position);
- }
- });
+ drawerSettingsListView.setOnItemClickListener((parent, view, position, id) -> selectItem(parent, position));
drawerSettingsListView.setAdapter(settingsListAdapter);
}
@@ -276,12 +265,7 @@ public class NavigationDrawerFragment extends Fragment {
settingsListAdapter.addItem(getSwitchInstance(getString(R.string.save_battery),
getSaveBattery(getContext()),
BATTERY_SAVER,
- new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean newStateIsChecked) {
- onSwitchItemSelected(BATTERY_SAVER, newStateIsChecked);
- }
- }));
+ (buttonView, newStateIsChecked) -> onSwitchItemSelected(BATTERY_SAVER, newStateIsChecked)));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
settingsListAdapter.addItem(getSimpleTextInstance(getString(R.string.always_on_vpn), ALWAYS_ON));
@@ -318,31 +302,25 @@ public class NavigationDrawerFragment extends Fragment {
private void showDottedIconWithDelay() {
final Handler navigationDrawerHandler = new Handler();
- navigationDrawerHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (!wasPaused) {
- toolbar.setNavigationIcon(R.drawable.ic_menu_color_point);
- toolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK);
- }
-
+ navigationDrawerHandler.postDelayed(() -> {
+ if (!wasPaused) {
+ toolbar.setNavigationIcon(R.drawable.ic_menu_color_point);
+ toolbar.playSoundEffect(android.view.SoundEffectConstants.CLICK);
}
+
}, THREE_SECONDS);
}
@NonNull
private void closeDrawerWithDelay() {
final Handler navigationDrawerHandler = new Handler();
- navigationDrawerHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (!wasPaused) {
- drawerLayout.closeDrawer(fragmentContainerView, true);
- } else {
- shouldCloseOnResume = true;
- }
-
+ navigationDrawerHandler.postDelayed(() -> {
+ if (!wasPaused) {
+ drawerLayout.closeDrawer(fragmentContainerView, true);
+ } else {
+ shouldCloseOnResume = true;
}
+
}, TWO_SECONDS);
}
@@ -382,31 +360,18 @@ public class NavigationDrawerFragment extends Fragment {
alertDialog = alertBuilder
.setTitle(activity.getString(R.string.save_battery))
.setMessage(activity.getString(R.string.save_battery_message))
- .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- DrawerSettingsItem item = settingsListAdapter.getDrawerItem(BATTERY_SAVER);
- item.setChecked(true);
- settingsListAdapter.notifyDataSetChanged();
- saveBattery(getContext(), item.isChecked());
- }
+ .setPositiveButton((android.R.string.yes), (dialog, which) -> {
+ DrawerSettingsItem item = settingsListAdapter.getDrawerItem(BATTERY_SAVER);
+ item.setChecked(true);
+ settingsListAdapter.notifyDataSetChanged();
+ saveBattery(getContext(), item.isChecked());
})
- .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- disableSwitch(BATTERY_SAVER);
- }
- }).setOnDismissListener(new DialogInterface.OnDismissListener() {
+ .setNegativeButton(activity.getString(android.R.string.no), (dialog, which) -> disableSwitch(BATTERY_SAVER)).setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
showEnableExperimentalFeature = false;
}
- }).setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- disableSwitch(BATTERY_SAVER);
- }
- }).show();
+ }).setOnCancelListener(dialog -> disableSwitch(BATTERY_SAVER)).show();
} catch (IllegalStateException e) {
e.printStackTrace();
}
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..2bd666bf 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
@@ -1,26 +1,52 @@
package se.leap.bitmaskclient.eip;
-import java.util.*;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import static se.leap.bitmaskclient.utils.ConfigHelper.getCurrentTimezone;
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) {
+ int i = 0;
+ for (Map.Entry<Integer,Set<Gateway>> entrySet : offsets.entrySet()) {
+ for (Gateway gateway : entrySet.getValue()) {
+ 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();
}
private TreeMap<Integer, Set<Gateway>> calculateOffsets() {
TreeMap<Integer, Set<Gateway>> offsets = new TreeMap<Integer, Set<Gateway>>();
- int localOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
+ int localOffset = getCurrentTimezone();
for (Gateway gateway : gateways) {
int dist = timezoneDistance(localOffset, gateway.getTimezone());
Set<Gateway> set = (offsets.get(dist) != null) ?
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/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java
index 326139c0..d1ac0eb3 100644
--- a/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/ConfigHelper.java
@@ -39,6 +39,7 @@ import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Calendar;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.ProviderAPI;
@@ -177,4 +178,8 @@ public class ConfigHelper {
public static boolean preferAnonymousUsage() {
return BuildConfig.priotize_anonymous_usage;
}
+
+ public static int getCurrentTimezone() {
+ return Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
+ }
}
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..44831049 100644
--- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java
@@ -2,7 +2,6 @@ package se.leap.bitmaskclient.utils;
import android.content.Context;
import android.content.SharedPreferences;
-import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -10,12 +9,6 @@ import android.support.annotation.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
-import java.io.ByteArrayInputStream;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@@ -239,6 +232,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/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..eb9626bc
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="foo">
+ <attr name="textColorError" format="color" />
+ </declare-styleable>
+</resources> \ 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/themes.xml b/app/src/main/res/values/themes.xml
index 70bc3e16..f3c55f41 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -21,4 +21,8 @@
<item name="android:windowBackground">@drawable/splash_page</item>
</style>
+ <style name="foo">
+ <item name="textColorError">@color/colorPrimary</item>
+ </style>
+
</resources>
diff --git a/app/src/main/res/values/untranslatable.xml b/app/src/main/res/values/untranslatable.xml
index 7270acf6..07cca36e 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