summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/providersetup
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2021-11-12 00:46:35 +0000
committercyberta <cyberta@riseup.net>2021-11-12 00:46:35 +0000
commitc5d722f555b952407dade3abb1ffd537e6747317 (patch)
treea9ebb8b33438589a33ed9ce54ade50371c9fe147 /app/src/main/java/se/leap/bitmaskclient/providersetup
parent571c0479f7400e56cfdb27408160d8a816cc8610 (diff)
parent8aeb4791b6e024de9aa9c61b574d8c798a3c0a2c (diff)
Merge branch 'tor-snowflake' into 'master'
tor-over-snowflake Closes #9045 See merge request leap/bitmask_android!138
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/providersetup')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java55
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java18
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java13
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java122
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiSetupBroadcastReceiver.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java27
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java59
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupInterface.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java216
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java32
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java25
13 files changed, 497 insertions, 80 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
index 23c750a3..709ca651 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java
@@ -20,12 +20,20 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.os.Build;
import androidx.annotation.NonNull;
import androidx.core.app.JobIntentService;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import java.util.concurrent.TimeoutException;
+
import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
+import se.leap.bitmaskclient.tor.TorServiceCommand;
+import se.leap.bitmaskclient.tor.TorServiceConnection;
import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
@@ -61,6 +69,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB
RECEIVER_KEY = "receiver",
ERRORS = "errors",
ERRORID = "errorId",
+ INITIAL_ACTION = "initalAction",
BACKEND_ERROR_KEY = "error",
BACKEND_ERROR_MESSAGE = "message",
USER_MESSAGE = "userMessage",
@@ -82,9 +91,12 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB
CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE = 15,
INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE = 16,
CORRECTLY_DOWNLOADED_GEOIP_JSON = 17,
- INCORRECTLY_DOWNLOADED_GEOIP_JSON = 18;
+ INCORRECTLY_DOWNLOADED_GEOIP_JSON = 18,
+ TOR_TIMEOUT = 19,
+ MISSING_NETWORK_CONNECTION = 20;
ProviderApiManager providerApiManager;
+ private volatile TorServiceConnection torServiceConnection;
//TODO: refactor me, please!
//used in insecure flavor only
@@ -120,9 +132,50 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
+ @Override
+ public boolean startTorService() throws InterruptedException, IllegalStateException, TimeoutException {
+ return TorServiceCommand.startTorService(this, null);
+ }
+
+ @Override
+ public void stopTorService() {
+ TorServiceCommand.stopTorService(this);
+ }
+
+ @Override
+ public int getTorHttpTunnelPort() {
+ return TorServiceCommand.getHttpTunnelPort(this);
+ }
+
+ @Override
+ public boolean hasNetworkConnection() {
+ try {
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ return activeNetwork != null &&
+ activeNetwork.isConnectedOrConnecting();
+ } else {
+ NetworkCapabilities capabilities = cm.getNetworkCapabilities(cm.getActiveNetwork());
+ if (capabilities != null) {
+ return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+ return false;
+ }
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ // we don't know, let's try to fetch data anyways then
+ return true;
+ }
+ }
+
+
+
private ProviderApiManager initApiManager() {
SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(getResources());
return new ProviderApiManager(preferences, getResources(), clientGenerator, this);
}
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java
index 1408dce8..3cdfcab0 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java
@@ -13,12 +13,12 @@ import se.leap.bitmaskclient.base.models.Provider;
public class ProviderAPICommand {
private static final String TAG = ProviderAPICommand.class.getSimpleName();
- private Context context;
+ private final Context context;
- private String action;
- private Bundle parameters;
- private ResultReceiver resultReceiver;
- private Provider provider;
+ private final String action;
+ private final Bundle parameters;
+ private final ResultReceiver resultReceiver;
+ private final Provider provider;
private ProviderAPICommand(@NonNull Context context, @NonNull String action, @NonNull Provider provider, ResultReceiver resultReceiver) {
this(context.getApplicationContext(), action, Bundle.EMPTY, provider, resultReceiver);
@@ -64,22 +64,22 @@ public class ProviderAPICommand {
return command;
}
- public static void execute(Context context, String action, @NonNull Provider provider) {
+ public static void execute(Context context, String action, Provider provider) {
ProviderAPICommand command = new ProviderAPICommand(context, action, provider);
command.execute();
}
- public static void execute(Context context, String action, Bundle parameters, @NonNull Provider provider) {
+ public static void execute(Context context, String action, Bundle parameters, Provider provider) {
ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider);
command.execute();
}
- public static void execute(Context context, String action, Bundle parameters, @NonNull Provider provider, ResultReceiver resultReceiver) {
+ public static void execute(Context context, String action, Bundle parameters, Provider provider, ResultReceiver resultReceiver) {
ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider, resultReceiver);
command.execute();
}
- public static void execute(Context context, String action, @NonNull Provider provider, ResultReceiver resultReceiver) {
+ public static void execute(Context context, String action, Provider provider, ResultReceiver resultReceiver) {
ProviderAPICommand command = new ProviderAPICommand(context, action, provider, resultReceiver);
command.execute();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java
index c863abd4..35ad9cd2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java
@@ -95,10 +95,15 @@ public class ProviderApiConnector {
if (!response.isSuccessful()) {
VpnStatus.logWarning("[API] API request failed: " + url);
}
- InputStream inputStream = response.body().byteStream();
- Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
- if (scanner.hasNext()) {
- return scanner.next();
+
+ if (response.body() != null) {
+ InputStream inputStream = response.body().byteStream();
+ Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
+ if (scanner.hasNext()) {
+ String result = scanner.next();
+ response.body().close();
+ return result;
+ }
}
return null;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
index c5dc6572..52046e07 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
@@ -48,6 +48,7 @@ import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.concurrent.TimeoutException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
@@ -59,10 +60,13 @@ import se.leap.bitmaskclient.base.models.Constants.CREDENTIAL_ERRORS;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
import se.leap.bitmaskclient.base.utils.ConfigHelper;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.eip.EipStatus;
import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
import se.leap.bitmaskclient.providersetup.models.LeapSRPSession;
import se.leap.bitmaskclient.providersetup.models.SrpCredentials;
import se.leap.bitmaskclient.providersetup.models.SrpRegistrationData;
+import se.leap.bitmaskclient.tor.TorStatusObservable;
import static se.leap.bitmaskclient.R.string.certificate_error;
import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
@@ -110,9 +114,11 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLO
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.INITIAL_ACTION;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.LOGOUT_FAILED;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.LOG_IN;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.LOG_OUT;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.MISSING_NETWORK_CONNECTION;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.PARAMETERS;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_NOK;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_OK;
@@ -122,12 +128,17 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.SIGN_UP;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.SUCCESSFUL_LOGIN;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.SUCCESSFUL_LOGOUT;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.SUCCESSFUL_SIGNUP;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_TIMEOUT;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_PROVIDER_DETAILS;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE;
import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING;
import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_TOR_TIMEOUT;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.ON;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getProxyPort;
/**
* Implements the logic of the http api calls. The methods of this class needs to be called from
@@ -140,9 +151,13 @@ public abstract class ProviderApiManagerBase {
public interface ProviderApiServiceCallback {
void broadcastEvent(Intent intent);
+ boolean startTorService() throws InterruptedException, IllegalStateException, TimeoutException;
+ void stopTorService();
+ int getTorHttpTunnelPort();
+ boolean hasNetworkConnection();
}
- private ProviderApiServiceCallback serviceCallback;
+ private final ProviderApiServiceCallback serviceCallback;
protected SharedPreferences preferences;
protected Resources resources;
@@ -156,7 +171,6 @@ public abstract class ProviderApiManagerBase {
}
public void handleIntent(Intent command) {
-// Log.d(TAG, "handleIntent was called!");
ResultReceiver receiver = null;
if (command.getParcelableExtra(RECEIVER_KEY) != null) {
receiver = command.getParcelableExtra(RECEIVER_KEY);
@@ -164,18 +178,42 @@ public abstract class ProviderApiManagerBase {
String action = command.getAction();
Bundle parameters = command.getBundleExtra(PARAMETERS);
- Provider provider = command.getParcelableExtra(PROVIDER_KEY);
+ if (action == null) {
+ Log.e(TAG, "Intent without action sent!");
+ return;
+ }
- if (provider == null) {
+ Provider provider = null;
+ if (command.getParcelableExtra(PROVIDER_KEY) != null) {
+ provider = command.getParcelableExtra(PROVIDER_KEY);
+ } else {
//TODO: consider returning error back e.g. NO_PROVIDER
Log.e(TAG, action +" called without provider!");
return;
}
- if (action == null) {
- Log.e(TAG, "Intent without action sent!");
+
+ if (!serviceCallback.hasNetworkConnection()) {
+ Bundle result = new Bundle();
+ setErrorResult(result, R.string.error_network_connection, null);
+ sendToReceiverOrBroadcast(receiver, MISSING_NETWORK_CONNECTION, result, provider);
return;
}
+ try {
+ if (PreferenceHelper.getUseBridges(preferences)) {
+ startTorProxy();
+ }
+ } catch (InterruptedException | IllegalStateException e) {
+ e.printStackTrace();
+ return;
+ } catch (TimeoutException e) {
+ serviceCallback.stopTorService();
+ Bundle result = new Bundle();
+ setErrorResult(result, R.string.error_tor_timeout, ERROR_TOR_TIMEOUT.toString(), action);
+ sendToReceiverOrBroadcast(receiver, TOR_TIMEOUT, result, provider);
+ return;
+ }
+
Bundle result = new Bundle();
switch (action) {
case UPDATE_PROVIDER_DETAILS:
@@ -269,7 +307,34 @@ public abstract class ProviderApiManagerBase {
}
ProviderObservable.getInstance().setProviderForDns(null);
}
+ break;
+ }
+ }
+
+ protected boolean startTorProxy() throws InterruptedException, IllegalStateException, TimeoutException {
+ if (EipStatus.getInstance().isDisconnected() &&
+ PreferenceHelper.getUseTor(preferences) &&
+ serviceCallback.startTorService()) {
+ waitForTorCircuits();
+ if (TorStatusObservable.isCancelled()) {
+ throw new InterruptedException("Cancelled Tor setup.");
+ }
+ int port = serviceCallback.getTorHttpTunnelPort();
+ TorStatusObservable.setProxyPort(port);
+ return port != -1;
+ }
+ return false;
+ }
+
+ private void waitForTorCircuits() throws InterruptedException, TimeoutException {
+ if (TorStatusObservable.getStatus() == ON) {
+ return;
}
+ TorStatusObservable.waitUntil(this::isTorOnOrCancelled, 180);
+ }
+
+ private boolean isTorOnOrCancelled() {
+ return TorStatusObservable.getStatus() == ON || TorStatusObservable.isCancelled();
}
void resetProviderDetails(Provider provider) {
@@ -302,10 +367,11 @@ public abstract class ProviderApiManagerBase {
}
}
- private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId) {
+ private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage, String errorId, String initialAction) {
try {
jsonObject.put(ERRORS, errorMessage);
- jsonObject.put(ERRORID, errorId);
+ jsonObject.putOpt(ERRORID, errorId);
+ jsonObject.putOpt(INITIAL_ACTION, initialAction);
} catch (JSONException e) {
e.printStackTrace();
}
@@ -342,7 +408,7 @@ public abstract class ProviderApiManagerBase {
private Bundle register(Provider provider, String username, String password) {
JSONObject stepResult = null;
- OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult);
+ OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), stepResult);
if (okHttpClient == null) {
return backendErrorNotification(stepResult, username);
}
@@ -401,7 +467,7 @@ public abstract class ProviderApiManagerBase {
String providerApiUrl = provider.getApiUrlWithVersion();
- OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult);
+ OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), stepResult);
if (okHttpClient == null) {
return backendErrorNotification(stepResult, username);
}
@@ -678,17 +744,24 @@ public abstract class ProviderApiManagerBase {
}
private boolean canConnect(Provider provider, Bundle result) {
+ return canConnect(provider, result, 0);
+ }
+
+ private boolean canConnect(Provider provider, Bundle result, int tries) {
JSONObject errorJson = new JSONObject();
String providerUrl = provider.getApiUrlString() + "/provider.json";
- OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), errorJson);
+ OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), errorJson);
if (okHttpClient == null) {
result.putString(ERRORS, errorJson.toString());
return false;
}
- try {
+ if (tries > 0) {
+ result.remove(ERRORS);
+ }
+ try {
return ProviderApiConnector.canConnect(okHttpClient, providerUrl);
} catch (UnknownHostException | SocketTimeoutException e) {
@@ -714,6 +787,19 @@ public abstract class ProviderApiManagerBase {
VpnStatus.logWarning("[API] IOException during connection check: " + e.getLocalizedMessage());
setErrorResult(result, error_io_exception_user_message, null);
}
+
+ try {
+ if (tries == 0 &&
+ result.containsKey(ERRORS) &&
+ TorStatusObservable.getStatus() == OFF &&
+ startTorProxy()
+ ) {
+ return canConnect(provider, result, 1);
+ }
+ } catch (InterruptedException | IllegalStateException | TimeoutException e) {
+ e.printStackTrace();
+ }
+
return false;
}
@@ -862,13 +948,13 @@ public abstract class ProviderApiManagerBase {
}
Bundle setErrorResult(Bundle result, int errorMessageId, String errorId) {
+ return setErrorResult(result, errorMessageId, errorId, null);
+ }
+
+ Bundle setErrorResult(Bundle result, int errorMessageId, String errorId, String initialAction) {
JSONObject errorJson = new JSONObject();
String errorMessage = getProviderFormattedString(resources, errorMessageId);
- if (errorId != null) {
- addErrorMessageToJson(errorJson, errorMessage, errorId);
- } else {
- addErrorMessageToJson(errorJson, errorMessage);
- }
+ addErrorMessageToJson(errorJson, errorMessage, errorId, initialAction);
VpnStatus.logWarning("[API] error: " + errorMessage);
result.putString(ERRORS, errorJson.toString());
result.putBoolean(BROADCAST_RESULT_KEY, false);
@@ -950,7 +1036,7 @@ public abstract class ProviderApiManagerBase {
}
private boolean logOut(Provider provider) {
- OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), new JSONObject());
+ OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), new JSONObject());
if (okHttpClient == null) {
return false;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiSetupBroadcastReceiver.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiSetupBroadcastReceiver.java
index 710aee0f..cc6ff149 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiSetupBroadcastReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiSetupBroadcastReceiver.java
@@ -67,8 +67,10 @@ public class ProviderApiSetupBroadcastReceiver extends BroadcastReceiver {
case ProviderAPI.PROVIDER_OK:
setupInterface.handleProviderSetUp(handledProvider);
break;
+ case ProviderAPI.MISSING_NETWORK_CONNECTION:
+ case ProviderAPI.TOR_TIMEOUT:
case ProviderAPI.PROVIDER_NOK:
- setupInterface.handleProviderSetupFailed(resultData);
+ setupInterface.handleError(resultData);
break;
case ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE:
setupInterface.handleCorrectlyDownloadedCertificate(handledProvider);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
index 654fb8e2..88413087 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
@@ -1,18 +1,15 @@
package se.leap.bitmaskclient.providersetup;
import android.content.res.AssetManager;
+
import androidx.annotation.VisibleForTesting;
import com.pedrogomez.renderers.AdapteeCollection;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -44,8 +41,8 @@ public class ProviderManager implements AdapteeCollection<Provider> {
private File externalFilesDir;
private Set<Provider> defaultProviders;
private Set<Provider> customProviders;
- private Set<URL> defaultProviderURLs;
- private Set<URL> customProviderURLs;
+ private Set<String> defaultProviderURLs;
+ private Set<String> customProviderURLs;
private static ProviderManager instance;
@@ -76,10 +73,10 @@ public class ProviderManager implements AdapteeCollection<Provider> {
}
}
- private Set<URL> getProviderUrlSetFromProviderSet(Set<Provider> providers) {
- HashSet<URL> providerUrls = new HashSet<>();
+ private Set<String> getProviderUrlSetFromProviderSet(Set<Provider> providers) {
+ HashSet<String> providerUrls = new HashSet<>();
for (Provider provider : providers) {
- providerUrls.add(provider.getMainUrl().getUrl());
+ providerUrls.add(provider.getMainUrl().toString());
}
return providerUrls;
}
@@ -167,16 +164,16 @@ public class ProviderManager implements AdapteeCollection<Provider> {
@Override
public boolean add(Provider element) {
return element != null &&
- !defaultProviderURLs.contains(element.getMainUrl().getUrl()) &&
+ !defaultProviderURLs.contains(element.getMainUrl().toString()) &&
customProviders.add(element) &&
- customProviderURLs.add(element.getMainUrl().getUrl());
+ customProviderURLs.add(element.getMainUrl().toString());
}
@Override
public boolean remove(Object element) {
return element instanceof Provider &&
customProviders.remove(element) &&
- customProviderURLs.remove(((Provider) element).getMainUrl().getUrl());
+ customProviderURLs.remove(((Provider) element).getMainUrl().toString());
}
@Override
@@ -186,7 +183,7 @@ public class ProviderManager implements AdapteeCollection<Provider> {
while (iterator.hasNext()) {
Provider p = (Provider) iterator.next();
addedAll = customProviders.add(p) &&
- customProviderURLs.add(p.getMainUrl().getUrl()) &&
+ customProviderURLs.add(p.getMainUrl().toString()) &&
addedAll;
}
return addedAll;
@@ -199,8 +196,8 @@ public class ProviderManager implements AdapteeCollection<Provider> {
try {
while (iterator.hasNext()) {
Provider p = (Provider) iterator.next();
- removedAll = ((defaultProviders.remove(p) && defaultProviderURLs.remove(p.getMainUrl().getUrl())) ||
- (customProviders.remove(p) && customProviderURLs.remove(p.getMainUrl().getUrl()))) &&
+ removedAll = ((defaultProviders.remove(p) && defaultProviderURLs.remove(p.getMainUrl().toString())) ||
+ (customProviders.remove(p) && customProviderURLs.remove(p.getMainUrl().toString()))) &&
removedAll;
}
} catch (ClassCastException e) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java
index 947d1182..a9247807 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupFailedDialog.java
@@ -21,19 +21,25 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import org.json.JSONObject;
-import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.DEFAULT;
-import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.valueOf;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORID;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.INITIAL_ACTION;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.SET_UP_PROVIDER;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_INVALID_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.UPDATE_PROVIDER_DETAILS;
+import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.DEFAULT;
+import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.valueOf;
/**
* Implements a dialog to show why a download failed.
@@ -43,11 +49,13 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
public class ProviderSetupFailedDialog extends DialogFragment {
public static String TAG = "downloaded_failed_dialog";
- private final static String KEY_PROVIDER = "key provider";
- private final static String KEY_REASON_TO_FAIL = "key reason to fail";
- private final static String KEY_DOWNLOAD_ERROR = "key download error";
+ private final static String KEY_PROVIDER = "key_provider";
+ private final static String KEY_REASON_TO_FAIL = "key_reason_to_fail";
+ private final static String KEY_DOWNLOAD_ERROR = "key_download_error";
+ private final static String KEY_INITAL_ACTION = "key_inital_action";
private String reasonToFail;
private DOWNLOAD_ERRORS downloadError = DEFAULT;
+ private String initialAction;
private Provider provider;
@@ -59,7 +67,8 @@ public class ProviderSetupFailedDialog extends DialogFragment {
ERROR_CORRUPTED_PROVIDER_JSON,
ERROR_INVALID_CERTIFICATE,
ERROR_CERTIFICATE_PINNING,
- ERROR_NEW_URL_NO_VPN_PROVIDER
+ ERROR_NEW_URL_NO_VPN_PROVIDER,
+ ERROR_TOR_TIMEOUT
}
/**
@@ -91,6 +100,10 @@ public class ProviderSetupFailedDialog extends DialogFragment {
} else if (testNewURL) {
dialogFragment.downloadError = DOWNLOAD_ERRORS.ERROR_NEW_URL_NO_VPN_PROVIDER;
}
+
+ if (errorJson.has(INITIAL_ACTION)) {
+ dialogFragment.initialAction = errorJson.getString(INITIAL_ACTION);
+ }
} catch (Exception e) {
e.printStackTrace();
dialogFragment.reasonToFail = dialogFragment.getString(R.string.error_io_exception_user_message);
@@ -125,6 +138,14 @@ public class ProviderSetupFailedDialog extends DialogFragment {
builder.setPositiveButton(R.string.retry, (dialog, id)
-> interfaceWithConfigurationWizard.addAndSelectNewProvider(provider.getMainUrlString()));
break;
+ case ERROR_TOR_TIMEOUT:
+ builder.setPositiveButton(R.string.retry, (dialog, id) -> {
+ handleTorTimeoutError();
+ });
+ builder.setNeutralButton(R.string.retry_unobfuscated, ((dialog, id) -> {
+ PreferenceHelper.useBridges(getContext(), false);
+ handleTorTimeoutError();
+ }));
default:
builder.setPositiveButton(R.string.retry, (dialog, id)
-> interfaceWithConfigurationWizard.retrySetUpProvider(provider));
@@ -135,6 +156,20 @@ public class ProviderSetupFailedDialog extends DialogFragment {
return builder.create();
}
+ private void handleTorTimeoutError() {
+ switch (initialAction) {
+ case SET_UP_PROVIDER:
+ case UPDATE_PROVIDER_DETAILS:
+ interfaceWithConfigurationWizard.retrySetUpProvider(provider);
+ break;
+ case UPDATE_INVALID_VPN_CERTIFICATE:
+ ProviderAPICommand.execute(getContext(), UPDATE_INVALID_VPN_CERTIFICATE, provider);
+ break;
+ default:
+ break;
+ }
+ }
+
public interface DownloadFailedDialogInterface {
void retrySetUpProvider(@NonNull Provider provider);
@@ -159,6 +194,12 @@ public class ProviderSetupFailedDialog extends DialogFragment {
}
@Override
+ public void onDetach() {
+ super.onDetach();
+ interfaceWithConfigurationWizard = null;
+ }
+
+ @Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
interfaceWithConfigurationWizard.cancelSettingUpProvider();
@@ -170,6 +211,7 @@ public class ProviderSetupFailedDialog extends DialogFragment {
outState.putParcelable(KEY_PROVIDER, provider);
outState.putString(KEY_REASON_TO_FAIL, reasonToFail);
outState.putString(KEY_DOWNLOAD_ERROR, downloadError.toString());
+ outState.putString(KEY_INITAL_ACTION, initialAction);
}
private void restoreFromSavedInstance(Bundle savedInstanceState) {
@@ -185,5 +227,8 @@ public class ProviderSetupFailedDialog extends DialogFragment {
if (savedInstanceState.containsKey(KEY_DOWNLOAD_ERROR)) {
this.downloadError = valueOf(savedInstanceState.getString(KEY_DOWNLOAD_ERROR));
}
+ if (savedInstanceState.containsKey(KEY_INITAL_ACTION)) {
+ this.initialAction = savedInstanceState.getString(KEY_INITAL_ACTION);
+ }
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupInterface.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupInterface.java
index 5b5c94b4..0c60a3ce 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupInterface.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupInterface.java
@@ -35,7 +35,7 @@ public interface ProviderSetupInterface {
}
void handleProviderSetUp(Provider provider);
- void handleProviderSetupFailed(Bundle resultData);
+ void handleError(Bundle resultData);
void handleCorrectlyDownloadedCertificate(Provider provider);
void handleIncorrectlyDownloadedCertificate();
Provider getProvider();
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
index b2f13e07..a4104e30 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java
@@ -5,31 +5,49 @@ import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Guideline;
import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+import java.util.Observable;
+import java.util.Observer;
import butterknife.BindView;
-import butterknife.Optional;
+import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.views.ProviderHeaderView;
+import se.leap.bitmaskclient.tor.TorStatusObservable;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getBootstrapProgress;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastLogs;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.getStringForCurrentStatus;
/**
* Base Activity for configuration wizard activities
@@ -37,7 +55,7 @@ import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
* Created by fupduck on 09.01.18.
*/
-public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity {
+public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity implements Observer {
private static final String TAG = ConfigWizardBaseActivity.class.getName();
public static final float GUIDE_LINE_COMPACT_DELTA = 0.1f;
@@ -52,12 +70,50 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity {
protected LinearLayout loadingScreen;
@Nullable
+ @BindView(R.id.btn_connection_detail)
+ protected AppCompatTextView connectionDetailBtn;
+
+ @Nullable
+ @BindView(R.id.connection_detail_header_container)
+ protected RelativeLayout connectionDetailHeaderContainer;
+
+ @Nullable
+ @BindView(R.id.connection_details_title)
+ protected AppCompatTextView connectionDetailsTitle;
+
+ @Nullable
+ @BindView(R.id.connection_detail_container)
+ protected RelativeLayout connectionDetailContainer;
+
+ @Nullable
+ @BindView(R.id.log_container)
+ protected RelativeLayout logsContainer;
+
+ @Nullable
+ @BindView(R.id.tor_state)
+ protected AppCompatTextView torState;
+
+ @Nullable
+ @BindView(R.id.snowflake_state)
+ protected AppCompatTextView snowflakeState;
+
+ @Nullable
+ @BindView(R.id.connection_detail_logs)
+ protected RecyclerView connectionDetailLogs;
+
+ private TorLogAdapter torLogAdapter;
+
+ @Nullable
@BindView(R.id.progressbar)
protected ProgressBar progressBar;
@Nullable
+ @BindView(R.id.progressbar_title)
+ protected AppCompatTextView progressbarTitle;
+
+ @Nullable
@BindView(R.id.progressbar_description)
- protected AppCompatTextView progressbarText;
+ protected AppCompatTextView progressbarDescription;
//Only tablet layouts have guidelines as they are based on a ConstraintLayout
@Nullable
@@ -142,12 +198,15 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity {
protected void onPause() {
super.onPause();
isActivityShowing = false;
+ TorStatusObservable.getInstance().deleteObserver(this);
}
@Override
protected void onResume() {
super.onResume();
isActivityShowing = true;
+ TorStatusObservable.getInstance().addObserver(this);
+ setProgressbarDescription(getStringForCurrentStatus(this));
}
protected void restoreState(Bundle savedInstanceState) {
@@ -168,10 +227,64 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity {
providerHeaderView.setTitle(providerHeaderText);
}
+ protected void hideConnectionDetails() {
+ if (loadingScreen == null) {
+ return;
+ }
+
+ connectionDetailHeaderContainer.setVisibility(GONE);
+ connectionDetailContainer.setVisibility(GONE);
+ logsContainer.setVisibility(GONE);
+ }
+
+ protected void showConnectionDetails() {
+ if (loadingScreen == null) {
+ return;
+ }
+ LinearLayoutManager layoutManager = new LinearLayoutManager(this);
+ connectionDetailLogs.setLayoutManager(layoutManager);
+ torLogAdapter = new TorLogAdapter(getLastLogs());
+ connectionDetailLogs.setAdapter(torLogAdapter);
+
+ connectionDetailLogs.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (newState != SCROLL_STATE_IDLE) {
+ torLogAdapter.postponeUpdate = true;
+ } else if (newState == SCROLL_STATE_IDLE && getFirstVisibleItemPosion() == 0) {
+ torLogAdapter.postponeUpdate = false;
+ }
+ }
+ });
+
+ snowflakeState.setText(getLastSnowflakeLog());
+ torState.setText(getLastTorLog());
+ connectionDetailBtn.setOnClickListener(v -> {
+ if (logsContainer.getVisibility() == VISIBLE) {
+ logsContainer.setVisibility(GONE);
+ connectionDetailContainer.setVisibility(GONE);
+ connectionDetailsTitle.setVisibility(GONE);
+ connectionDetailBtn.setText(R.string.show_connection_details);
+ } else {
+ logsContainer.setVisibility(VISIBLE);
+ connectionDetailContainer.setVisibility(VISIBLE);
+ connectionDetailsTitle.setVisibility(VISIBLE);
+ connectionDetailBtn.setText(R.string.hide_connection_details);
+ }
+ });
+ connectionDetailHeaderContainer.setVisibility(VISIBLE);
+ }
+
+ private int getFirstVisibleItemPosion() {
+ return ((LinearLayoutManager)connectionDetailLogs.getLayoutManager()).findFirstVisibleItemPosition();
+ }
+
protected void hideProgressBar() {
if (loadingScreen == null) {
return;
}
+ hideConnectionDetails();
loadingScreen.setVisibility(GONE);
content.setVisibility(VISIBLE);
}
@@ -184,11 +297,30 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity {
loadingScreen.setVisibility(VISIBLE);
}
- protected void setProgressbarText(@StringRes int progressbarText) {
- if (this.progressbarText == null) {
+ protected void setProgressbarTitle(@StringRes int progressbarTitle) {
+ if (loadingScreen == null) {
+ return;
+ }
+ this.progressbarTitle.setText(progressbarTitle);
+ }
+
+ protected void setProgressbarDescription(String progressbarDescription) {
+ if (loadingScreen == null) {
+ return;
+ }
+ this.progressbarDescription.setText(progressbarDescription);
+ }
+
+ protected void setConfigProgress(int value) {
+ if (loadingScreen == null) {
return;
}
- this.progressbarText.setText(progressbarText);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+ progressBar.setProgress(value);
+ } else {
+ progressBar.setProgress(value, true);
+ }
+ progressBar.setIndeterminate(value >= 100 || value < 0);
}
@@ -287,4 +419,76 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity {
});
}
+ @Override
+ public void update(Observable o, Object arg) {
+ if (o instanceof TorStatusObservable) {
+ runOnUiThread(() -> {
+ if (TorStatusObservable.getStatus() != TorStatusObservable.TorStatus.OFF && loadingScreen != null) {
+ if (connectionDetailContainer.getVisibility() == GONE) {
+ showConnectionDetails();
+ } else {
+ setLogs(getLastTorLog(), getLastSnowflakeLog(), getLastLogs());
+ }
+ }
+ setProgressbarDescription(getStringForCurrentStatus(ConfigWizardBaseActivity.this));
+ setConfigProgress(getBootstrapProgress());
+ });
+ }
+ }
+
+ protected void setLogs(String torLog, String snowflakeLog, List<String> lastLogs) {
+ if (loadingScreen == null) {
+ return;
+ }
+ torLogAdapter.updateData(lastLogs);
+ torState.setText(torLog);
+ snowflakeState.setText(snowflakeLog);
+ }
+
+ static class TorLogAdapter extends RecyclerView.Adapter<TorLogAdapter.ViewHolder> {
+ private List<String> values;
+ private boolean postponeUpdate;
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ public AppCompatTextView logTextLabel;
+ public View layout;
+
+ public ViewHolder(View v) {
+ super(v);
+ layout = v;
+ logTextLabel = v.findViewById(android.R.id.text1);
+ }
+ }
+
+ public void updateData(List<String> data) {
+ values = data;
+ if (!postponeUpdate) {
+ notifyDataSetChanged();
+ }
+ }
+
+ public TorLogAdapter(List<String> data) {
+ values = data;
+ }
+
+ @NonNull
+ @Override
+ public TorLogAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(
+ parent.getContext());
+ View v = inflater.inflate(R.layout.v_log_item, parent, false);
+ return new TorLogAdapter.ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(TorLogAdapter.ViewHolder holder, final int position) {
+ final String log = values.get(position);
+ holder.logTextLabel.setText(log);
+ }
+
+ @Override
+ public int getItemCount() {
+ return values.size();
+ }
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java
index a8bac6d8..9a5f31f2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java
@@ -17,7 +17,7 @@ public class LoginActivity extends ProviderCredentialsBaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setProgressbarText(R.string.logging_in);
+ setProgressbarTitle(R.string.logging_in);
setProviderHeaderLogo(R.drawable.logo);
setProviderHeaderText(R.string.login_to_profile);
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java
index 40efd811..e429f776 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java
@@ -20,6 +20,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -29,15 +30,18 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.json.JSONException;
import org.json.JSONObject;
+import org.torproject.jni.TorService;
import se.leap.bitmaskclient.base.FragmentManagerEnhanced;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.providersetup.ProviderAPICommand;
-import se.leap.bitmaskclient.providersetup.ProviderDetailActivity;
import se.leap.bitmaskclient.providersetup.ProviderApiSetupBroadcastReceiver;
+import se.leap.bitmaskclient.providersetup.ProviderDetailActivity;
import se.leap.bitmaskclient.providersetup.ProviderManager;
import se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog;
import se.leap.bitmaskclient.providersetup.ProviderSetupInterface;
+import se.leap.bitmaskclient.tor.TorServiceCommand;
+import se.leap.bitmaskclient.tor.TorStatusObservable;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_PROVIDER_API_EVENT;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
@@ -51,13 +55,14 @@ import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.Provide
import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.ProviderConfigState.SETTING_UP_PROVIDER;
import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.ProviderConfigState.SHOWING_PROVIDER_DETAILS;
import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.ProviderConfigState.SHOW_FAILED_DIALOG;
+import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF;
/**
* Created by cyberta on 19.08.18.
*/
public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity implements ProviderSetupInterface, ProviderSetupFailedDialog.DownloadFailedDialogInterface {
- final public static String TAG = "PoviderSetupActivity";
+ final public static String TAG = "ProviderSetupActivity";
final private static String ACTIVITY_STATE = "ACTIVITY STATE";
final private static String REASON_TO_FAIL = "REASON TO FAIL";
@@ -86,11 +91,13 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity
showProgressBar();
} else if (PENDING_SHOW_FAILED_DIALOG == providerConfigState) {
showProgressBar();
+ hideConnectionDetails();
showDownloadFailedDialog();
} else if (SHOW_FAILED_DIALOG == providerConfigState) {
showProgressBar();
+ hideConnectionDetails();
} else if (SHOWING_PROVIDER_DETAILS == providerConfigState) {
- cancelSettingUpProvider();
+ cancelSettingUpProvider(false);
} else if (PENDING_SHOW_PROVIDER_DETAILS == providerConfigState) {
showProviderDetails();
}
@@ -142,7 +149,7 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity
}
@Override
- public void handleProviderSetupFailed(Bundle resultData) {
+ public void handleError(Bundle resultData) {
reasonToFail = resultData.getString(ERRORS);
showDownloadFailedDialog();
}
@@ -156,9 +163,7 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity
// -------- DownloadFailedDialogInterface ---v
@Override
public void cancelSettingUpProvider() {
- providerConfigState = PROVIDER_NOT_SET;
- provider = null;
- hideProgressBar();
+ cancelSettingUpProvider(true);
}
@Override
@@ -167,6 +172,16 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity
ProviderAPICommand.execute(this, UPDATE_PROVIDER_DETAILS, provider);
}
+ public void cancelSettingUpProvider(boolean stopTor) {
+ if (stopTor && TorStatusObservable.getStatus() != OFF) {
+ Log.d(TAG, "SHUTDOWN - cancelSettingUpProvider stopTor:" + stopTor);
+ TorServiceCommand.stopTorServiceAsync(this);
+ }
+ providerConfigState = PROVIDER_NOT_SET;
+ provider = null;
+ hideProgressBar();
+ }
+
protected void restoreState(Bundle savedInstanceState) {
super.restoreState(savedInstanceState);
if (savedInstanceState == null) {
@@ -196,7 +211,7 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity
/**
* Once selected a provider, this fragment offers the user to log in,
* use it anonymously (if possible)
- * or cancel his/her election pressing the back button.
+ * or cancel their selection pressing the back button.
*/
public void showProviderDetails() {
// show only if current activity is shown
@@ -218,6 +233,7 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity
public void showDownloadFailedDialog() {
try {
providerConfigState = SHOW_FAILED_DIALOG;
+ hideConnectionDetails();
FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(ProviderSetupFailedDialog.TAG);
DialogFragment newFragment;
try {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java
index c0245845..16007a70 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java
@@ -37,7 +37,7 @@ public class SignupActivity extends ProviderCredentialsBaseActivity {
setProviderHeaderLogo(R.drawable.logo);
setProviderHeaderText(R.string.create_profile);
- setProgressbarText(R.string.signing_up);
+ setProgressbarTitle(R.string.signing_up);
setButtonText(R.string.signup_button);
passwordVerificationField.setVisibility(VISIBLE);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
index 2077a8b9..ea619263 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
@@ -18,6 +18,7 @@
package se.leap.bitmaskclient.providersetup.connectivity;
import android.content.res.Resources;
+import android.net.LocalSocketAddress;
import android.os.Build;
import androidx.annotation.NonNull;
@@ -26,6 +27,9 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
@@ -50,6 +54,7 @@ import static se.leap.bitmaskclient.R.string.certificate_error;
import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message;
import static se.leap.bitmaskclient.R.string.keyChainAccessError;
+import static se.leap.bitmaskclient.R.string.proxy;
import static se.leap.bitmaskclient.R.string.server_unreachable_message;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
@@ -61,34 +66,35 @@ import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormatted
public class OkHttpClientGenerator {
Resources resources;
+ private final static String PROXY_HOST = "127.0.0.1";
public OkHttpClientGenerator(/*SharedPreferences preferences,*/ Resources resources) {
this.resources = resources;
}
- public OkHttpClient initCommercialCAHttpClient(JSONObject initError) {
- return initHttpClient(initError, null);
+ public OkHttpClient initCommercialCAHttpClient(JSONObject initError, int proxyPort) {
+ return initHttpClient(initError, null, proxyPort);
}
- public OkHttpClient initSelfSignedCAHttpClient(String caCert, JSONObject initError) {
- return initHttpClient(initError, caCert);
+ public OkHttpClient initSelfSignedCAHttpClient(String caCert, int proxyPort, JSONObject initError) {
+ return initHttpClient(initError, caCert, proxyPort);
}
public OkHttpClient init() {
try {
- return createClient(null);
+ return createClient(null, -1);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
- private OkHttpClient initHttpClient(JSONObject initError, String certificate) {
+ private OkHttpClient initHttpClient(JSONObject initError, String certificate, int proxyPort) {
if (resources == null) {
return null;
}
try {
- return createClient(certificate);
+ return createClient(certificate, proxyPort);
} catch (IllegalArgumentException e) {
e.printStackTrace();
// TODO ca cert is invalid - show better error ?!
@@ -117,7 +123,7 @@ public class OkHttpClientGenerator {
return null;
}
- private OkHttpClient createClient(String certificate) throws Exception {
+ private OkHttpClient createClient(String certificate, int proxyPort) throws Exception {
TLSCompatSocketFactory sslCompatFactory;
ConnectionSpec spec = getConnectionSpec();
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
@@ -131,6 +137,9 @@ public class OkHttpClientGenerator {
clientBuilder.cookieJar(getCookieJar())
.connectionSpecs(Collections.singletonList(spec));
clientBuilder.dns(new DnsResolver());
+ if (proxyPort != -1) {
+ clientBuilder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, proxyPort)));
+ }
return clientBuilder.build();
}