summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/providersetup
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/providersetup')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java134
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java17
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java46
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java18
7 files changed, 144 insertions, 81 deletions
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 35ad9cd2..cc875c79 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java
@@ -18,6 +18,8 @@
package se.leap.bitmaskclient.providersetup;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
import android.util.Pair;
import java.io.IOException;
@@ -26,6 +28,9 @@ import java.util.List;
import java.util.Locale;
import java.util.Scanner;
+import javax.net.ssl.SSLHandshakeException;
+
+import de.blinkt.openvpn.core.NativeUtils;
import de.blinkt.openvpn.core.VpnStatus;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
@@ -39,72 +44,103 @@ import okhttp3.Response;
public class ProviderApiConnector {
- private static final MediaType JSON
- = MediaType.parse("application/json; charset=utf-8");
+ public interface ProviderApiConnectorInterface {
+ boolean delete(OkHttpClient okHttpClient, String deleteUrl) throws RuntimeException, IOException;
+ boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException;
+ String requestStringFromServer(@NonNull String url, @NonNull String requestMethod, String jsonString, @NonNull List<Pair<String, String>> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException;
+
+ }
+ public static class DefaultProviderApiCpnnector implements ProviderApiConnectorInterface {
+
+ @Override
+ public boolean delete(OkHttpClient okHttpClient, String deleteUrl) {
+ try {
+ Request.Builder requestBuilder = new Request.Builder()
+ .url(deleteUrl)
+ .delete();
+ Request request = requestBuilder.build();
+
+ Response response = okHttpClient.newCall(request).execute();
+ //response code 401: already logged out
+ if (response.isSuccessful() || response.code() == 401) {
+ return true;
+ }
+ } catch (IOException | RuntimeException e) {
+ return false;
+ }
+
+ return false;
+ }
- public static boolean delete(OkHttpClient okHttpClient, String deleteUrl) {
- try {
+ @Override
+ public boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException {
Request.Builder requestBuilder = new Request.Builder()
- .url(deleteUrl)
- .delete();
+ .url(url)
+ .method("GET", null);
Request request = requestBuilder.build();
Response response = okHttpClient.newCall(request).execute();
- //response code 401: already logged out
- if (response.isSuccessful() || response.code() == 401) {
- return true;
+ if (!response.isSuccessful()) {
+ VpnStatus.logWarning("[API] API request failed canConnect(): " + url);
}
- } catch (IOException | RuntimeException e) {
- return false;
+ return response.isSuccessful();
}
- return false;
- }
+ @Override
+ public String requestStringFromServer(@NonNull String url, @NonNull String requestMethod, String jsonString, @NonNull List<Pair<String, String>> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException {
+ RequestBody jsonBody = jsonString != null ? RequestBody.create(JSON, jsonString) : null;
+ Request.Builder requestBuilder = new Request.Builder()
+ .url(url)
+ .method(requestMethod, jsonBody);
+ for (Pair<String, String> keyValPair : headerArgs) {
+ requestBuilder.addHeader(keyValPair.first, keyValPair.second);
+ }
- public static boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException {
- Request.Builder requestBuilder = new Request.Builder()
- .url(url)
- .method("GET", null);
- Request request = requestBuilder.build();
-
- Response response = okHttpClient.newCall(request).execute();
- if (!response.isSuccessful()) {
- VpnStatus.logWarning("[API] API request failed canConnect(): " + url);
+ //TODO: move to getHeaderArgs()?
+ String locale = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry();
+ requestBuilder.addHeader("Accept-Language", locale);
+ Request request = requestBuilder.build();
+
+ Response response = okHttpClient.newCall(request).execute();
+ if (!response.isSuccessful()) {
+ VpnStatus.logWarning("[API] API request failed: " + url);
+ }
+
+ 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;
}
- return response.isSuccessful();
+ }
+ private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+ private static ProviderApiConnectorInterface instance = new DefaultProviderApiCpnnector();
+ @VisibleForTesting
+ public ProviderApiConnector(ProviderApiConnectorInterface connectorInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("ProviderApiConnector injected with ProviderApiConnectorInterface outside of an unit test");
+ }
+ instance = connectorInterface;
}
- public static String requestStringFromServer(@NonNull String url, @NonNull String request_method, String jsonString, @NonNull List<Pair<String, String>> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException {
- RequestBody jsonBody = jsonString != null ? RequestBody.create(JSON, jsonString) : null;
- Request.Builder requestBuilder = new Request.Builder()
- .url(url)
- .method(request_method, jsonBody);
- for (Pair<String, String> keyValPair : headerArgs) {
- requestBuilder.addHeader(keyValPair.first, keyValPair.second);
- }
+ public static boolean delete(OkHttpClient okHttpClient, String deleteUrl) throws RuntimeException, IOException {
+ return instance.delete(okHttpClient, deleteUrl);
+ }
- //TODO: move to getHeaderArgs()?
- String locale = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry();
- requestBuilder.addHeader("Accept-Language", locale);
- Request request = requestBuilder.build();
+ public static boolean canConnect(@NonNull OkHttpClient okHttpClient, String url) throws RuntimeException, IOException {
+ return instance.canConnect(okHttpClient, url);
- Response response = okHttpClient.newCall(request).execute();
- if (!response.isSuccessful()) {
- VpnStatus.logWarning("[API] API request failed: " + url);
- }
+ }
- 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;
+ public static String requestStringFromServer(@NonNull String url, @NonNull String requestMethod, String jsonString, @NonNull List<Pair<String, String>> headerArgs, @NonNull OkHttpClient okHttpClient) throws RuntimeException, IOException {
+ return instance.requestStringFromServer(url, requestMethod, jsonString, headerArgs, okHttpClient);
}
}
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 93648bb0..1f737b0c 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
@@ -45,9 +45,10 @@ import static se.leap.bitmaskclient.base.models.Provider.CA_CERT;
import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.RSAHelper.parseRsaKeyFromString;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.getTorTimeout;
+import static se.leap.bitmaskclient.base.utils.RSAHelper.parseRsaKeyFromString;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getDomainFromMainURL;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.getFingerprintFromCertificate;
+import static se.leap.bitmaskclient.base.utils.CertificateHelper.getFingerprintFromCertificate;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getFromPersistedProvider;
@@ -380,7 +381,7 @@ public abstract class ProviderApiManagerBase {
if (TorStatusObservable.getStatus() == ON) {
return;
}
- TorStatusObservable.waitUntil(this::isTorOnOrCancelled, 180);
+ TorStatusObservable.waitUntil(this::isTorOnOrCancelled, getTorTimeout());
}
private boolean isTorOnOrCancelled() {
@@ -1138,9 +1139,13 @@ public abstract class ProviderApiManagerBase {
String deleteUrl = provider.getApiUrlWithVersion() + "/logout";
- if (ProviderApiConnector.delete(okHttpClient, deleteUrl)) {
- LeapSRPSession.setToken("");
- return true;
+ try {
+ if (ProviderApiConnector.delete(okHttpClient, deleteUrl)) {
+ LeapSRPSession.setToken("");
+ return true;
+ }
+ } catch (IOException e) {
+ // eat me
}
return false;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java
index 90a32fea..c882b0bb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderSetupObservable.java
@@ -16,14 +16,19 @@ package se.leap.bitmaskclient.providersetup;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import android.os.Handler;
-import android.os.Looper;
-
-import java.util.Observable;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import se.leap.bitmaskclient.base.utils.HandlerProvider;
+import se.leap.bitmaskclient.base.utils.HandlerProvider.HandlerInterface;
import se.leap.bitmaskclient.tor.TorStatusObservable;
-public class ProviderSetupObservable extends Observable {
+/**
+ * This Observable tracks the progress of a started provider bootstrapping attempt.
+ * Each required API call us taken into account as well as the state of tor's bootstrapping in case
+ * it is used for censorship circumvention.
+ */
+public class ProviderSetupObservable {
private static final String TAG = ProviderSetupObservable.class.getSimpleName();
@@ -36,9 +41,28 @@ public class ProviderSetupObservable extends Observable {
public static final int DOWNLOADED_VPN_CERTIFICATE = 100;
private static ProviderSetupObservable instance;
- private final Handler handler = new Handler(Looper.getMainLooper());
+ private final PropertyChangeSupport changeSupport;
+ public static final String PROPERTY_CHANGE = "ProviderSetupObservable";
+ private final HandlerInterface handler;
private long lastUpdate = 0;
+
+
+ private ProviderSetupObservable() {
+ handler = HandlerProvider.get();
+ changeSupport = new PropertyChangeSupport(this);
+
+ }
+
+ public void addObserver(PropertyChangeListener propertyChangeListener) {
+ changeSupport.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ public void deleteObserver(PropertyChangeListener propertyChangeListener) {
+ changeSupport.removePropertyChangeListener(propertyChangeListener);
+ }
+
+
public static ProviderSetupObservable getInstance() {
if (instance == null) {
instance = new ProviderSetupObservable();
@@ -58,8 +82,8 @@ public class ProviderSetupObservable extends Observable {
getInstance().progress = progress;
}
- getInstance().setChanged();
- getInstance().notifyObservers();
+ getInstance().changeSupport.firePropertyChange(PROPERTY_CHANGE, null, getInstance());
+
}, now - getInstance().lastUpdate < 500L ? 500L : 0L);
getInstance().lastUpdate = System.currentTimeMillis() + 500;
}
@@ -72,8 +96,7 @@ public class ProviderSetupObservable extends Observable {
getInstance().handler.postDelayed(() -> {
getInstance().progress = (TorStatusObservable.getBootstrapProgress()) / 2;
- getInstance().setChanged();
- getInstance().notifyObservers();
+ getInstance().changeSupport.firePropertyChange(PROPERTY_CHANGE, null, getInstance());
}, now - getInstance().lastUpdate < 500L ? 500L : 0);
getInstance().lastUpdate = System.currentTimeMillis() + 500;
}
@@ -84,8 +107,7 @@ public class ProviderSetupObservable extends Observable {
public static void reset() {
getInstance().progress = 0;
- getInstance().setChanged();
- getInstance().notifyObservers();
+ getInstance().changeSupport.firePropertyChange(PROPERTY_CHANGE, null, getInstance());
}
public static void cancel() {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java
index 00630f39..fb190dc2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/SetupViewPagerAdapter.java
@@ -1,6 +1,6 @@
package se.leap.bitmaskclient.providersetup;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.isDefaultBitmask;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.isDefaultBitmask;
import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CIRCUMVENTION_SETUP_FRAGMENT;
import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CONFIGURE_PROVIDER_FRAGMENT;
import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.NOTIFICATION_PERMISSON_EDUCATIONAL_FRAGMENT;
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java
index b258a100..9235daad 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SetupActivity.java
@@ -3,7 +3,7 @@ package se.leap.bitmaskclient.providersetup.activities;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static androidx.appcompat.app.ActionBar.DISPLAY_SHOW_CUSTOM;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.isDefaultBitmask;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.isDefaultBitmask;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences;
import static se.leap.bitmaskclient.providersetup.fragments.SetupFragmentFactory.CONFIGURE_PROVIDER_FRAGMENT;
import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF;
@@ -100,7 +100,7 @@ public class SetupActivity extends AppCompatActivity implements SetupActivityCal
// indicator views for VPN permission
- Intent requestVpnPermission = VpnService.prepare(this);
+ Intent requestVpnPermission = VpnService.prepare(this.getApplicationContext());
if (requestVpnPermission != null) {
addIndicatorView(indicatorViews);
addIndicatorView(indicatorViews);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
index 34a93319..cdb8bd78 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
@@ -1,8 +1,7 @@
package se.leap.bitmaskclient.providersetup.fragments;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.isDefaultBitmask;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.isDefaultBitmask;
-import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -17,7 +16,6 @@ import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.databinding.FCircumventionSetupBinding;
import se.leap.bitmaskclient.providersetup.ProviderManager;
import se.leap.bitmaskclient.providersetup.activities.CancelCallback;
-import se.leap.bitmaskclient.providersetup.activities.SetupActivityCallback;
public class CircumventionSetupFragment extends BaseSetupFragment implements CancelCallback {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
index ec646cac..8477c302 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
@@ -9,7 +9,7 @@ import static se.leap.bitmaskclient.R.string.description_configure_provider_circ
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.isDefaultBitmask;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.isDefaultBitmask;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake;
import static se.leap.bitmaskclient.base.utils.ViewHelper.animateContainerVisibility;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE;
@@ -42,9 +42,9 @@ import androidx.core.content.res.ResourcesCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.util.List;
-import java.util.Observable;
-import java.util.Observer;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Provider;
@@ -57,7 +57,7 @@ import se.leap.bitmaskclient.providersetup.TorLogAdapter;
import se.leap.bitmaskclient.providersetup.activities.CancelCallback;
import se.leap.bitmaskclient.tor.TorStatusObservable;
-public class ConfigureProviderFragment extends BaseSetupFragment implements Observer, CancelCallback, EipSetupListener {
+public class ConfigureProviderFragment extends BaseSetupFragment implements PropertyChangeListener, CancelCallback, EipSetupListener {
private static final String TAG = ConfigureProviderFragment.class.getSimpleName();
@@ -158,8 +158,8 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse
}
@Override
- public void update(Observable o, Object arg) {
- if (o instanceof ProviderSetupObservable) {
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (ProviderSetupObservable.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
Activity activity = getActivity();
if (activity == null || binding == null) {
return;
@@ -206,7 +206,7 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse
if (ignoreProviderAPIUpdates ||
provider == null ||
(setupActivityCallback.getSelectedProvider() != null &&
- !setupActivityCallback.getSelectedProvider().getDomain().equals(provider.getDomain()))) {
+ !setupActivityCallback.getSelectedProvider().getMainUrlString().equals(provider.getMainUrlString()))) {
return;
}
@@ -222,7 +222,9 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Obse
setupActivityCallback.onProviderSelected(provider);
handler.postDelayed(() -> {
if (!ProviderSetupObservable.isCanceled()) {
- setupActivityCallback.onConfigurationSuccess();
+ if (setupActivityCallback != null) {
+ setupActivityCallback.onConfigurationSuccess();
+ }
}
}, 750);
break;