summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/VpnProfile.java5
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/BitmaskTileService.java13
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java36
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java13
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/ProviderObservable.java24
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java98
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/CertificateHelper.java64
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java136
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java41
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/HandlerProvider.java38
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java24
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/RSAHelper.java72
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/TimezoneHelper.java47
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java47
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java11
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java14
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java13
-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
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java26
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java40
41 files changed, 744 insertions, 345 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
index 780ac9d8..9da1e452 100644
--- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
@@ -16,7 +16,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
-import android.preference.PreferenceManager;
import android.security.KeyChain;
import android.security.KeyChainException;
import android.text.TextUtils;
@@ -77,6 +76,7 @@ import de.blinkt.openvpn.core.connection.ConnectionAdapter;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.ProviderObservable;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
public class VpnProfile implements Serializable, Cloneable {
// Note that this class cannot be moved to core where it belongs since
@@ -651,8 +651,7 @@ public class VpnProfile implements Serializable, Cloneable {
if (mPushPeerInfo)
cfg.append("push-peer-info\n");
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- boolean usesystemproxy = prefs.getBoolean("usesystemproxy", true);
+ boolean usesystemproxy = PreferenceHelper.useSystemProxy();
if (usesystemproxy && !mIsOpenVPN22 && !configForOvpn3 && !usesExtraProxyOptions()) {
cfg.append("# Use system proxy setting\n");
cfg.append("management-query-proxy\n");
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index b38eeb14..5a618f10 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -9,7 +9,7 @@ import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT;
import static de.blinkt.openvpn.core.NetworkSpace.IpAddress;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn;
import android.Manifest.permission;
import android.app.Notification;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java b/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java
index d152031a..19ea180d 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/connection/Obfs4Connection.java
@@ -1,6 +1,6 @@
package de.blinkt.openvpn.core.connection;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn;
import static se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient.DISPATCHER_IP;
import static se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient.DISPATCHER_PORT;
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskTileService.java b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskTileService.java
index 370a7af6..d85e0a75 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskTileService.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskTileService.java
@@ -8,6 +8,8 @@ import android.os.Build;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.util.Observable;
import java.util.Observer;
@@ -19,7 +21,7 @@ import se.leap.bitmaskclient.base.models.ProviderObservable;
@TargetApi(Build.VERSION_CODES.N)
-public class BitmaskTileService extends TileService implements Observer {
+public class BitmaskTileService extends TileService implements PropertyChangeListener {
@SuppressLint("Override")
@TargetApi(Build.VERSION_CODES.N)
@@ -59,7 +61,7 @@ public class BitmaskTileService extends TileService implements Observer {
public void onStartListening() {
super.onStartListening();
EipStatus.getInstance().addObserver(this);
- update(EipStatus.getInstance(), null);
+ propertyChange(new PropertyChangeEvent(EipStatus.getInstance(), EipStatus.PROPERTY_CHANGE, null, EipStatus.getInstance()));
}
@Override
@@ -69,16 +71,15 @@ public class BitmaskTileService extends TileService implements Observer {
}
@Override
- public void update(Observable o, Object arg) {
+ public void propertyChange(PropertyChangeEvent evt) {
Tile t = getQsTile();
// Tile t should never be null according to https://developer.android.com/reference/kotlin/android/service/quicksettings/TileService.
// Hovever we've got crash reports.
if (t == null) {
return;
}
-
- if (o instanceof EipStatus) {
- EipStatus status = (EipStatus) o;
+ if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ EipStatus status = (EipStatus) evt.getNewValue();
Icon icon;
String title;
if (status.isConnecting() || status.isReconnecting()) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
index 5da238d4..3f541d8d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
@@ -59,8 +59,8 @@ import androidx.fragment.app.FragmentTransaction;
import org.json.JSONException;
import org.json.JSONObject;
-import java.util.Observable;
-import java.util.Observer;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
@@ -80,7 +80,7 @@ import se.leap.bitmaskclient.eip.EipSetupListener;
import se.leap.bitmaskclient.eip.EipSetupObserver;
import se.leap.bitmaskclient.providersetup.ProviderAPI;
-public class MainActivity extends AppCompatActivity implements EipSetupListener, Observer {
+public class MainActivity extends AppCompatActivity implements EipSetupListener, PropertyChangeListener {
public final static String TAG = MainActivity.class.getSimpleName();
@@ -354,9 +354,9 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener,
}
@Override
- public void update(Observable o, Object arg) {
- if (o instanceof ProviderObservable) {
- this.provider = ((ProviderObservable) o).getCurrentProvider();
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (ProviderObservable.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ this.provider = (Provider) evt.getNewValue();
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java
index f4e09e62..fb93796e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java
@@ -56,8 +56,8 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback;
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
-import java.util.Observable;
-import java.util.Observer;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.VpnStatus;
@@ -80,7 +80,7 @@ import se.leap.bitmaskclient.providersetup.activities.SetupActivity;
import se.leap.bitmaskclient.tor.TorServiceCommand;
import se.leap.bitmaskclient.tor.TorStatusObservable;
-public class EipFragment extends Fragment implements Observer {
+public class EipFragment extends Fragment implements PropertyChangeListener {
public final static String TAG = EipFragment.class.getSimpleName();
@@ -375,17 +375,27 @@ public class EipFragment extends Fragment implements Observer {
}
+
@Override
- public void update(Observable observable, Object data) {
- if (observable instanceof EipStatus) {
- previousEipLevel = eipStatus.getEipLevel();
- eipStatus = (EipStatus) observable;
- handleNewStateOnMain();
-
- } else if (observable instanceof ProviderObservable) {
- provider = ((ProviderObservable) observable).getCurrentProvider();
- } else if (observable instanceof TorStatusObservable && EipStatus.getInstance().isUpdatingVpnCert()) {
- handleNewStateOnMain();
+ public void propertyChange(PropertyChangeEvent evt) {
+ switch (evt.getPropertyName()) {
+ case ProviderObservable.PROPERTY_CHANGE: {
+ provider = ((Provider) evt.getNewValue());
+ break;
+ }
+ case TorStatusObservable.PROPERTY_CHANGE: {
+ if (EipStatus.getInstance().isUpdatingVpnCert()) {
+ handleNewStateOnMain();
+ }
+ break;
+ }
+ case EipStatus.PROPERTY_CHANGE: {
+ previousEipLevel = eipStatus.getEipLevel();
+ eipStatus = (EipStatus) evt.getNewValue();
+ handleNewStateOnMain();
+ break;
+ }
+ default: {}
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java
index 99b1ac39..bb5a06c4 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/GatewaySelectionFragment.java
@@ -44,6 +44,8 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Observable;
@@ -64,7 +66,7 @@ interface LocationListSelectionListener {
void onLocationManuallySelected(Location location);
}
-public class GatewaySelectionFragment extends Fragment implements Observer, LocationListSelectionListener, SharedPreferences.OnSharedPreferenceChangeListener {
+public class GatewaySelectionFragment extends Fragment implements PropertyChangeListener, LocationListSelectionListener, SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = GatewaySelectionFragment.class.getSimpleName();
@@ -197,9 +199,9 @@ public class GatewaySelectionFragment extends Fragment implements Observer, Loca
}
@Override
- public void update(Observable o, Object arg) {
- if (o instanceof EipStatus) {
- eipStatus = (EipStatus) o;
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ eipStatus = (EipStatus) evt.getNewValue();
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(this::updateRecommendedLocation);
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java
index 60c21c40..16aea065 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java
@@ -24,7 +24,7 @@ import static se.leap.bitmaskclient.base.models.Constants.ENABLE_DONATION;
import static se.leap.bitmaskclient.base.models.Constants.PREFERRED_CITY;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_SWITCH_PROVIDER;
-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.getPreferredCity;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getSaveBattery;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.saveBattery;
@@ -53,8 +53,8 @@ import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
-import java.util.Observable;
-import java.util.Observer;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
@@ -74,7 +74,7 @@ import se.leap.bitmaskclient.tethering.TetheringObservable;
* See the <a href="https://developer.android.com/design/patterns/navigation-drawer.html#Interaction">
* design guidelines</a> for a complete explanation of the behaviors implemented here.
*/
-public class NavigationDrawerFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener, Observer {
+public class NavigationDrawerFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener, PropertyChangeListener {
/**
* Per the design guidelines, you should show the drawer on launch until the user manually
@@ -444,8 +444,8 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen
}
@Override
- public void update(Observable o, Object arg) {
- if (o instanceof TetheringObservable || o instanceof EipStatus) {
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName()) || TetheringObservable.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
try {
getActivity().runOnUiThread(() ->
enableSaveBatteryEntry(!TetheringObservable.getInstance().getTetheringState().isVpnTetheringRunning()));
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java
index 948d764f..7d12ca70 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ObfuscationProxyDialog.java
@@ -15,7 +15,7 @@ import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.AppCompatEditText;
-import se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper;
+import se.leap.bitmaskclient.base.utils.BuildConfigHelper;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.base.views.IconSwitchEntry;
import se.leap.bitmaskclient.databinding.DObfuscationProxyBinding;
@@ -68,12 +68,12 @@ public class ObfuscationProxyDialog extends AppCompatDialogFragment {
dismiss();
});
- useDefaultsButton.setVisibility(ObfsVpnHelper.hasObfuscationPinningDefaults() ? VISIBLE : GONE);
+ useDefaultsButton.setVisibility(BuildConfigHelper.hasObfuscationPinningDefaults() ? VISIBLE : GONE);
useDefaultsButton.setOnClickListener(v -> {
- ipField.setText(ObfsVpnHelper.obfsvpnIP());
- portField.setText(ObfsVpnHelper.obfsvpnPort());
- certificateField.setText(ObfsVpnHelper.obfsvpnCert());
- kcpSwitch.setChecked(ObfsVpnHelper.useKcp());
+ ipField.setText(BuildConfigHelper.obfsvpnIP());
+ portField.setText(BuildConfigHelper.obfsvpnPort());
+ certificateField.setText(BuildConfigHelper.obfsvpnCert());
+ kcpSwitch.setChecked(BuildConfigHelper.useKcp());
});
cancelButton.setOnClickListener(v -> {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
index c8c994a5..d7b62de2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
@@ -8,7 +8,7 @@ import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP;
import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL;
import static se.leap.bitmaskclient.base.models.Constants.USE_OBFUSCATION_PINNING;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.isCalyxOSWithTetheringSupport;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.allowExperimentalTransports;
import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getExcludedApps;
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java
index 588daa3f..05744bc9 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/TetheringDialog.java
@@ -22,8 +22,8 @@ import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import java.util.Observable;
-import java.util.Observer;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -52,7 +52,7 @@ import se.leap.bitmaskclient.tethering.TetheringObservable;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-public class TetheringDialog extends AppCompatDialogFragment implements Observer {
+public class TetheringDialog extends AppCompatDialogFragment implements PropertyChangeListener {
public final static String TAG = TetheringDialog.class.getName();
@@ -241,9 +241,9 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer
}
@Override
- public void update(Observable o, Object arg) {
- if (o instanceof TetheringObservable) {
- TetheringObservable observable = (TetheringObservable) o;
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (TetheringObservable.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ TetheringObservable observable = (TetheringObservable) evt.getNewValue();
Log.d(TAG, "TetheringObservable is updated");
dataset[0].enabled = observable.isWifiTetheringEnabled();
dataset[1].enabled = observable.isUsbTetheringEnabled();
@@ -251,5 +251,4 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer
adapter.notifyDataSetChanged();
}
}
-
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
index 70bf3943..18590f0b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
@@ -52,6 +52,7 @@ public interface Constants {
String OBFUSCATION_PINNING_CERT = "obfuscation_pinning_cert";
String OBFUSCATION_PINNING_KCP = "obfuscation_pinning_udp";
String OBFUSCATION_PINNING_LOCATION = "obfuscation_pinning_location";
+ String USE_SYSTEM_PROXY = "usesystemproxy";
//////////////////////////////////////////////
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
index 14c78cc3..cb9bd520 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java
@@ -28,8 +28,8 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOWED_REGIS
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_ALLOW_ANONYMOUS;
import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT;
import static se.leap.bitmaskclient.base.models.Constants.TYPE;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.RSAHelper.parseRsaKeyFromString;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn;
+import static se.leap.bitmaskclient.base.utils.RSAHelper.parseRsaKeyFromString;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
import android.os.Parcel;
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/ProviderObservable.java b/app/src/main/java/se/leap/bitmaskclient/base/models/ProviderObservable.java
index 3e1e1fcc..6e28ac3e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/ProviderObservable.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/ProviderObservable.java
@@ -2,13 +2,17 @@ package se.leap.bitmaskclient.base.models;
import androidx.annotation.NonNull;
-import java.util.Observable;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
/**
* Created by cyberta on 05.12.18.
*/
-public class ProviderObservable extends Observable {
+public class ProviderObservable {
private static ProviderObservable instance;
+ private final PropertyChangeSupport changeSupport;
+ public static final String PROPERTY_CHANGE = "ProviderObservable";
+
private Provider currentProvider;
private Provider providerForDns;
@@ -19,11 +23,23 @@ public class ProviderObservable extends Observable {
return instance;
}
+ private ProviderObservable() {
+ changeSupport = new PropertyChangeSupport(this);
+ currentProvider = new Provider();
+ }
+
+ public void addObserver(PropertyChangeListener propertyChangeListener) {
+ changeSupport.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ public void deleteObserver(PropertyChangeListener propertyChangeListener) {
+ changeSupport.removePropertyChangeListener(propertyChangeListener);
+ }
+
public synchronized void updateProvider(@NonNull Provider provider) {
instance.currentProvider = provider;
instance.providerForDns = null;
- instance.setChanged();
- instance.notifyObservers();
+ instance.changeSupport.firePropertyChange(PROPERTY_CHANGE, null, provider);
}
public Provider getCurrentProvider() {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java
new file mode 100644
index 00000000..e1f65b5e
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/BuildConfigHelper.java
@@ -0,0 +1,98 @@
+package se.leap.bitmaskclient.base.utils;
+
+import static se.leap.bitmaskclient.base.models.Constants.DEFAULT_BITMASK;
+
+import androidx.annotation.VisibleForTesting;
+
+import de.blinkt.openvpn.core.NativeUtils;
+import se.leap.bitmaskclient.BuildConfig;
+
+// ObfsVpnHelper class allows us to mock BuildConfig fields related to the pre-shipped circumvention settings
+public class BuildConfigHelper {
+
+ public interface BuildConfigHelperInterface {
+ boolean useObfsVpn();
+ boolean hasObfuscationPinningDefaults();
+ String obfsvpnIP();
+ String obfsvpnPort();
+ String obfsvpnCert();
+ boolean useKcp();
+ boolean isDefaultBitmask();
+ }
+
+ public static class DefaultBuildConfigHelper implements BuildConfigHelperInterface {
+ @Override
+ public boolean useObfsVpn() {
+ return BuildConfig.use_obfsvpn;
+ }
+
+ @Override
+ public boolean hasObfuscationPinningDefaults() {
+ return BuildConfig.obfsvpn_ip != null &&
+ BuildConfig.obfsvpn_port != null &&
+ BuildConfig.obfsvpn_cert != null &&
+ !BuildConfig.obfsvpn_ip.isEmpty() &&
+ !BuildConfig.obfsvpn_port.isEmpty() &&
+ !BuildConfig.obfsvpn_cert.isEmpty();
+ }
+
+ @Override
+ public String obfsvpnIP() {
+ return BuildConfig.obfsvpn_ip;
+ }
+
+ @Override
+ public String obfsvpnPort() {
+ return BuildConfig.obfsvpn_port;
+ }
+
+ @Override
+ public String obfsvpnCert() {
+ return BuildConfig.obfsvpn_cert;
+ }
+
+ @Override
+ public boolean useKcp() {
+ return BuildConfig.obfsvpn_use_kcp;
+ }
+
+ @Override
+ public boolean isDefaultBitmask() {
+ return BuildConfig.FLAVOR_branding.equals(DEFAULT_BITMASK);
+ }
+ }
+
+ private static BuildConfigHelperInterface instance = new DefaultBuildConfigHelper();
+
+ @VisibleForTesting
+ public BuildConfigHelper(BuildConfigHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("ObfsVpnHelper injected with ObfsVpnHelperInterface outside of an unit test");
+ }
+ instance = helperInterface;
+ }
+
+ public static boolean useObfsVpn() {
+ return instance.useObfsVpn();
+ }
+
+ public static boolean hasObfuscationPinningDefaults() {
+ return instance.hasObfuscationPinningDefaults();
+ }
+ public static String obfsvpnIP() {
+ return instance.obfsvpnIP();
+ }
+ public static String obfsvpnPort() {
+ return instance.obfsvpnPort();
+ }
+ public static String obfsvpnCert() {
+ return instance.obfsvpnCert();
+ }
+ public static boolean useKcp() {
+ return instance.useKcp();
+ }
+
+ public static boolean isDefaultBitmask() {
+ return instance.isDefaultBitmask();
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/CertificateHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/CertificateHelper.java
new file mode 100644
index 00000000..11202734
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/CertificateHelper.java
@@ -0,0 +1,64 @@
+package se.leap.bitmaskclient.base.utils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import de.blinkt.openvpn.core.NativeUtils;
+
+public class CertificateHelper {
+
+ public interface CertificateHelperInterface {
+ String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException;
+
+ }
+
+ public static class DefaultCertificateHelper implements CertificateHelperInterface {
+
+ public String byteArrayToHex(byte[] input) {
+ int readBytes = input.length;
+ StringBuffer hexData = new StringBuffer();
+ int onebyte;
+ for (int i = 0; i < readBytes; i++) {
+ onebyte = ((0x000000ff & input[i]) | 0xffffff00);
+ hexData.append(Integer.toHexString(onebyte).substring(6));
+ }
+ return hexData.toString();
+ }
+
+ /**
+ * Calculates the hexadecimal representation of a sha256/sha1 fingerprint of a certificate
+ *
+ * @param certificate
+ * @param encoding
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws CertificateEncodingException
+ */
+ @Override
+ public String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException {
+ byte[] byteArray = MessageDigest.getInstance(encoding).digest(certificate.getEncoded());
+ return byteArrayToHex(byteArray);
+ }
+ }
+
+ private static CertificateHelperInterface instance = new DefaultCertificateHelper();
+
+ @VisibleForTesting
+ public CertificateHelper(CertificateHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("CertificateHelper injected with CertificateHelperInterface outside of an unit test");
+ }
+ instance = helperInterface;
+ }
+
+ @NonNull
+ public static String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException {
+ return instance.getFingerprintFromCertificate(certificate, encoding);
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
index 9289738a..cd5d1fca 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
@@ -16,8 +16,6 @@
*/
package se.leap.bitmaskclient.base.utils;
-import static se.leap.bitmaskclient.base.models.Constants.DEFAULT_BITMASK;
-
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
@@ -36,22 +34,14 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
-import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import de.blinkt.openvpn.core.NativeUtils;
import okhttp3.internal.publicsuffix.PublicSuffixDatabase;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
@@ -69,8 +59,6 @@ public class ConfigHelper {
final public static String NG_1024 =
"eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
final public static BigInteger G = new BigInteger("2");
- final public static Pattern IPv4_PATTERN = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$");
- final public static Pattern PEM_CERTIFICATE_PATTERN = Pattern.compile("((-----BEGIN CERTIFICATE-----)([A-Za-z0-9+/=\\n]+)(-----END CERTIFICATE-----)+)");
public static boolean checkErroneousDownload(String downloadedString) {
try {
@@ -109,8 +97,8 @@ public class ConfigHelper {
CertificateFactory cf;
try {
cf = CertificateFactory.getInstance("X.509");
-
- Matcher matcher = PEM_CERTIFICATE_PATTERN.matcher(certificateString);
+ Pattern pattern = Pattern.compile("((-----BEGIN CERTIFICATE-----)([A-Za-z0-9+/=\\n]+)(-----END CERTIFICATE-----)+)");
+ Matcher matcher = pattern.matcher(certificateString);
while (matcher.find()) {
String certificate = matcher.group(3);
if (certificate == null) continue;
@@ -131,65 +119,6 @@ public class ConfigHelper {
return null;
}
- public static class RSAHelper {
- public static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
- RSAPrivateKey key;
- try {
- KeyFactory kf;
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
- kf = KeyFactory.getInstance("RSA", "BC");
- } else {
- kf = KeyFactory.getInstance("RSA");
- }
- rsaKeyString = rsaKeyString.replaceFirst("-----BEGIN RSA PRIVATE KEY-----", "").replaceFirst("-----END RSA PRIVATE KEY-----", "");
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(rsaKeyString));
- key = (RSAPrivateKey) kf.generatePrivate(keySpec);
- } catch (InvalidKeySpecException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- } catch (NoSuchAlgorithmException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- } catch (NullPointerException e) {
- e.printStackTrace();
- return null;
- } catch (NoSuchProviderException e) {
- e.printStackTrace();
- return null;
- }
-
- return key;
- }
- }
-
- private static String byteArrayToHex(byte[] input) {
- int readBytes = input.length;
- StringBuffer hexData = new StringBuffer();
- int onebyte;
- for (int i = 0; i < readBytes; i++) {
- onebyte = ((0x000000ff & input[i]) | 0xffffff00);
- hexData.append(Integer.toHexString(onebyte).substring(6));
- }
- return hexData.toString();
- }
-
- /**
- * Calculates the hexadecimal representation of a sha256/sha1 fingerprint of a certificate
- *
- * @param certificate
- * @param encoding
- * @return
- * @throws NoSuchAlgorithmException
- * @throws CertificateEncodingException
- */
- @NonNull
- public static String getFingerprintFromCertificate(X509Certificate certificate, String encoding) throws NoSuchAlgorithmException, CertificateEncodingException /*, UnsupportedEncodingException*/ {
- byte[] byteArray = MessageDigest.getInstance(encoding).digest(certificate.getEncoded());
- return byteArrayToHex(byteArray);
- }
-
public static void ensureNotOnMainThread(@NonNull Context context) throws IllegalStateException{
Looper looper = Looper.myLooper();
if (looper != null && looper == context.getMainLooper()) {
@@ -198,35 +127,18 @@ public class ConfigHelper {
}
}
- public static boolean isDefaultBitmask() {
- return BuildConfig.FLAVOR_branding.equals(DEFAULT_BITMASK);
- }
-
public static boolean preferAnonymousUsage() {
return BuildConfig.priotize_anonymous_usage;
}
- public static int getCurrentTimezone() {
- return Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
- }
-
- public static int timezoneDistance(int local_timezone, int remoteTimezone) {
- // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
- int dist = Math.abs(local_timezone - remoteTimezone);
- // Farther than 12 timezones and it's shorter around the "back"
- if (dist > 12)
- dist = 12 - (dist - 12); // Well i'll be. Absolute values make equations do funny things.
- return dist;
- }
-
/**
*
* @param remoteTimezone
* @return a value between 0.1 and 1.0
*/
public static double getConnectionQualityFromTimezoneDistance(int remoteTimezone) {
- int localTimeZone = ConfigHelper.getCurrentTimezone();
- int distance = ConfigHelper.timezoneDistance(localTimeZone, remoteTimezone);
+ int localTimeZone = TimezoneHelper.getCurrentTimezone();
+ int distance = TimezoneHelper.timezoneDistance(localTimeZone, remoteTimezone);
return Math.max(distance / 12.0, 0.1);
}
@@ -274,7 +186,7 @@ public class ConfigHelper {
if (ipv4 == null) {
return false;
}
- Matcher matcher = IPv4_PATTERN.matcher(ipv4);
+ Matcher matcher = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$").matcher(ipv4);
return matcher.matches();
}
@@ -287,35 +199,6 @@ public class ConfigHelper {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
}
- // ObfsVpnHelper class allows us to mock BuildConfig.use_obfsvpn while
- // not mocking the whole ConfigHelper class
- public static class ObfsVpnHelper {
- public static boolean useObfsVpn() {
- return BuildConfig.use_obfsvpn;
- }
-
- public static boolean hasObfuscationPinningDefaults() {
- return BuildConfig.obfsvpn_ip != null &&
- BuildConfig.obfsvpn_port != null &&
- BuildConfig.obfsvpn_cert != null &&
- !BuildConfig.obfsvpn_ip.isEmpty() &&
- !BuildConfig.obfsvpn_port.isEmpty() &&
- !BuildConfig.obfsvpn_cert.isEmpty();
- }
- public static String obfsvpnIP() {
- return BuildConfig.obfsvpn_ip;
- }
- public static String obfsvpnPort() {
- return BuildConfig.obfsvpn_port;
- }
- public static String obfsvpnCert() {
- return BuildConfig.obfsvpn_cert;
- }
- public static boolean useKcp() {
- return BuildConfig.obfsvpn_use_kcp;
- }
- }
-
public static int getPendingIntentFlags() {
int flags = PendingIntent.FLAG_CANCEL_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -323,4 +206,11 @@ public class ConfigHelper {
}
return flags;
}
+
+ public static int getTorTimeout() {
+ if (NativeUtils.isUnitTest()) {
+ return 1;
+ }
+ return 180;
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java
index eb1c255c..f1d86876 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/FileHelper.java
@@ -2,6 +2,8 @@ package se.leap.bitmaskclient.base.utils;
import android.content.Context;
+import androidx.annotation.VisibleForTesting;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
@@ -9,19 +11,50 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import de.blinkt.openvpn.core.NativeUtils;
+
/**
* Created by cyberta on 18.03.18.
*/
public class FileHelper {
+
+ public interface FileHelperInterface {
+ File createFile(File dir, String fileName);
+ void persistFile(File file, String content) throws IOException;
+ }
+
+ public static class DefaultFileHelper implements FileHelperInterface {
+ @Override
+ public File createFile(File dir, String fileName) {
+ return new File(dir, fileName);
+ }
+
+ @Override
+ public void persistFile(File file, String content) throws IOException {
+ FileWriter writer = new FileWriter(file);
+ writer.write(content);
+ writer.close();
+ }
+ }
+
+ private static FileHelperInterface instance = new DefaultFileHelper();
+
+ @VisibleForTesting
+ public FileHelper(FileHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("FileHelper injected with FileHelperInterface outside of an unit test");
+ }
+
+ instance = helperInterface;
+ }
+
public static File createFile(File dir, String fileName) {
- return new File(dir, fileName);
+ return instance.createFile(dir, fileName);
}
public static void persistFile(File file, String content) throws IOException {
- FileWriter writer = new FileWriter(file);
- writer.write(content);
- writer.close();
+ instance.persistFile(file, content);
}
public static String readPublicKey(Context context) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/HandlerProvider.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/HandlerProvider.java
new file mode 100644
index 00000000..d9198ab7
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/HandlerProvider.java
@@ -0,0 +1,38 @@
+package se.leap.bitmaskclient.base.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+
+public class HandlerProvider {
+
+
+ public interface HandlerInterface {
+ void postDelayed(Runnable r, long delay);
+ }
+
+
+ private static HandlerInterface instance;
+
+ public HandlerProvider(HandlerInterface handlerInterface) {
+ instance = handlerInterface;
+ }
+ public static HandlerInterface get() {
+ if (instance == null) {
+ instance = new DefaultHandler();
+ }
+ return instance;
+ }
+
+ public static class DefaultHandler implements HandlerInterface {
+ Handler handler;
+
+ public DefaultHandler() {
+ this.handler = new Handler(Looper.getMainLooper());
+ }
+ @Override
+ public void postDelayed(Runnable r, long delay) {
+ this.handler.postDelayed(r, delay);
+ }
+ }
+}
+
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
index 8e6273a7..6dfe0861 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
@@ -8,14 +8,36 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import de.blinkt.openvpn.core.NativeUtils;
+
/**
* Created by cyberta on 18.03.18.
*/
public class InputStreamHelper {
+ public interface InputStreamHelperInterface {
+ InputStream getInputStreamFrom(String filePath) throws FileNotFoundException;
+
+ }
+
+ private static InputStreamHelperInterface instance = new DefaultInputStreamHelper();
+
+ private static class DefaultInputStreamHelper implements InputStreamHelperInterface {
+ @Override
+ public InputStream getInputStreamFrom(String filePath) throws FileNotFoundException {
+ return new FileInputStream(filePath);
+ }
+ }
+
+ public InputStreamHelper(InputStreamHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("InputStreamHelper injected with InputStreamHelperInterface outside of an unit test");
+ }
+ instance = helperInterface;
+ }
//allows us to mock FileInputStream
public static InputStream getInputStreamFrom(String filePath) throws FileNotFoundException {
- return new FileInputStream(filePath);
+ return instance.getInputStreamFrom(filePath);
}
public static String loadInputStreamAsString(InputStream is) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
index b35a04cd..2420a797 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
@@ -40,6 +40,7 @@ import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL;
import static se.leap.bitmaskclient.base.models.Constants.USE_OBFUSCATION_PINNING;
import static se.leap.bitmaskclient.base.models.Constants.USE_SNOWFLAKE;
+import static se.leap.bitmaskclient.base.models.Constants.USE_SYSTEM_PROXY;
import android.content.Context;
import android.content.SharedPreferences;
@@ -460,12 +461,16 @@ public class PreferenceHelper {
return getBoolean(ALLOW_EXPERIMENTAL_TRANSPORTS, false);
}
+ public static boolean useSystemProxy() {
+ return getBoolean(USE_SYSTEM_PROXY, true);
+ }
+
public static void setUseObfuscationPinning(Boolean pinning) {
putBoolean(USE_OBFUSCATION_PINNING, pinning);
}
public static boolean useObfuscationPinning() {
- return ConfigHelper.ObfsVpnHelper.useObfsVpn() &&
+ return BuildConfigHelper.useObfsVpn() &&
getUseBridges() &&
getBoolean(USE_OBFUSCATION_PINNING, false) &&
!TextUtils.isEmpty(getObfuscationPinningIP()) &&
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/RSAHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/RSAHelper.java
new file mode 100644
index 00000000..2872139a
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/RSAHelper.java
@@ -0,0 +1,72 @@
+package se.leap.bitmaskclient.base.utils;
+
+import android.os.Build;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.spongycastle.util.encoders.Base64;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+import de.blinkt.openvpn.core.NativeUtils;
+
+public class RSAHelper {
+
+ public interface RSAHelperInterface {
+ RSAPrivateKey parseRsaKeyFromString(String rsaKeyString);
+ }
+
+ public static class DefaultRSAHelper implements RSAHelperInterface {
+
+ @Override
+ public RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
+ RSAPrivateKey key;
+ try {
+ KeyFactory kf;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
+ kf = KeyFactory.getInstance("RSA", "BC");
+ } else {
+ kf = KeyFactory.getInstance("RSA");
+ }
+ rsaKeyString = rsaKeyString.replaceFirst("-----BEGIN RSA PRIVATE KEY-----", "").replaceFirst("-----END RSA PRIVATE KEY-----", "");
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(rsaKeyString));
+ key = (RSAPrivateKey) kf.generatePrivate(keySpec);
+ } catch (InvalidKeySpecException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ return null;
+ } catch (NoSuchProviderException e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ return key;
+ }
+ }
+
+ private static RSAHelperInterface instance = new DefaultRSAHelper();
+
+ @VisibleForTesting
+ public RSAHelper(RSAHelperInterface helperInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("RSAHelper injected with RSAHelperInterface outside of an unit test");
+ }
+ instance = helperInterface;
+ }
+
+ public static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
+ return instance.parseRsaKeyFromString(rsaKeyString);
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/TimezoneHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/TimezoneHelper.java
new file mode 100644
index 00000000..63b12fd3
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/TimezoneHelper.java
@@ -0,0 +1,47 @@
+package se.leap.bitmaskclient.base.utils;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.Calendar;
+
+import de.blinkt.openvpn.core.NativeUtils;
+
+public class TimezoneHelper {
+
+ public interface TimezoneInterface {
+ int getCurrentTimezone();
+ }
+
+ private static TimezoneInterface instance = new DefaultTimezoneHelper();
+
+ @VisibleForTesting
+ public TimezoneHelper(TimezoneInterface timezoneInterface) {
+ if (!NativeUtils.isUnitTest()) {
+ throw new IllegalStateException("TimezoneHelper injected with timezoneInterface outside of an unit test");
+ }
+ instance = timezoneInterface;
+ }
+
+ public static TimezoneInterface get() {
+ return instance;
+ }
+
+ public static int timezoneDistance(int localTimezone, int remoteTimezone) { // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
+ int dist = Math.abs(localTimezone - remoteTimezone);
+ // Farther than 12 timezones and it's shorter around the "back"
+ if (dist > 12)
+ dist = 12 - (dist - 12); // Well i'll be. Absolute values make equations do funny things.
+ return dist;
+ }
+
+ public static int getCurrentTimezone() {
+ return get().getCurrentTimezone();
+ }
+
+ private static class DefaultTimezoneHelper implements TimezoneInterface {
+ @Override
+ public int getCurrentTimezone() {
+ return Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
+ }
+ }
+}
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 d8905bca..ed61ca13 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -67,6 +67,8 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.json.JSONException;
import org.json.JSONObject;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.io.Closeable;
import java.lang.ref.WeakReference;
import java.util.Observable;
@@ -97,7 +99,7 @@ import se.leap.bitmaskclient.eip.GatewaysManager.GatewayOptions;
* @author Sean Leonard <meanderingcode@aetherislands.net>
* @author Parménides GV <parmegv@sdf.org>
*/
-public final class EIP extends JobIntentService implements Observer {
+public final class EIP extends JobIntentService implements PropertyChangeListener {
public final static String TAG = EIP.class.getSimpleName(),
@@ -160,9 +162,9 @@ public final class EIP extends JobIntentService implements Observer {
* update eipStatus whenever it changes
*/
@Override
- public void update(Observable observable, Object data) {
- if (observable instanceof EipStatus) {
- eipStatus = (EipStatus) observable;
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ eipStatus = (EipStatus) evt.getNewValue();
}
}
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 9244f531..c2ba8af3 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
@@ -20,10 +20,10 @@ import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK;
import android.content.Context;
import android.os.AsyncTask;
-import androidx.annotation.VisibleForTesting;
import android.util.Log;
-import java.util.Observable;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.LogItem;
@@ -34,7 +34,7 @@ import de.blinkt.openvpn.core.VpnStatus;
* EipStatus changes it's state (EipLevel) when ConnectionStatus gets updated by OpenVpnService or
* by VoidVpnService.
*/
-public class EipStatus extends Observable implements VpnStatus.StateListener {
+public class EipStatus implements VpnStatus.StateListener {
public static String TAG = EipStatus.class.getSimpleName();
private static EipStatus currentStatus;
@@ -60,6 +60,9 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
private int localizedResId;
private boolean isUpdatingVPNCertificate;
+ private final PropertyChangeSupport propertyChange;
+ public static final String PROPERTY_CHANGE = "EipStatus";
+
public static EipStatus getInstance() {
if (currentStatus == null) {
currentStatus = new EipStatus();
@@ -69,18 +72,18 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
}
private EipStatus() {
+ propertyChange = new PropertyChangeSupport(this);
}
@Override
public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
- ConnectionStatus tmp = currentStatus.getLevel();
- currentStatus = getInstance();
- currentStatus.setState(state);
- currentStatus.setLogMessage(logmessage);
- currentStatus.setLocalizedResId(localizedResId);
- currentStatus.setLevel(level);
- currentStatus.setEipLevel(level);
- if (tmp != currentStatus.getLevel() || "RECONNECTING".equals(state) || "UI_CONNECTING".equals(state)) {
+ ConnectionStatus tmp = getInstance().getLevel();
+ getInstance().setState(state);
+ getInstance().setLogMessage(logmessage);
+ getInstance().setLocalizedResId(localizedResId);
+ getInstance().setLevel(level);
+ getInstance().setEipLevel(level);
+ if (tmp != getInstance().getLevel() || "RECONNECTING".equals(state) || "UI_CONNECTING".equals(state)) {
refresh();
}
}
@@ -90,13 +93,13 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
}
public boolean isReconnecting() {
- Log.d(TAG, "eip currentVPNStatus : " + currentStatus.getState() );
- return "RECONNECTING".equals(currentStatus.getState());
+ Log.d(TAG, "eip currentVPNStatus : " + getInstance().getState() );
+ return "RECONNECTING".equals(getInstance().getState());
}
public boolean isVPNRunningWithoutNetwork() {
- return currentStatus.getLevel() == LEVEL_NONETWORK &&
- !"NO_PROCESS".equals(currentStatus.getState());
+ return getInstance().getLevel() == LEVEL_NONETWORK &&
+ !"NO_PROCESS".equals(getInstance().getState());
}
private void setEipLevel(ConnectionStatus level) {
@@ -147,7 +150,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
* @param futureLevel
*/
private void setEipLevelWithDelay(ConnectionStatus futureLevel) {
- new DelayTask(currentStatus.getLevel(), futureLevel).execute();
+ new DelayTask(getInstance().getLevel(), futureLevel).execute();
}
private static class DelayTask extends AsyncTask<Void, Void, Void> {
@@ -169,7 +172,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
}
protected void onPostExecute(Void result) {
- if (currentLevel == currentStatus.getLevel()) {
+ if (currentLevel == getInstance().getLevel()) {
switch (futureLevel) {
case LEVEL_NONETWORK:
currentEipLevel = EipLevel.DISCONNECTED;
@@ -301,8 +304,14 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
}
public static void refresh() {
- currentStatus.setChanged();
- currentStatus.notifyObservers();
+ currentStatus.propertyChange.firePropertyChange(PROPERTY_CHANGE, null, currentStatus);
+ }
+
+ public void addObserver(PropertyChangeListener propertyChangeListener) {
+ propertyChange.addPropertyChangeListener(propertyChangeListener);
}
+ public void deleteObserver(PropertyChangeListener propertyChangeListener) {
+ propertyChange.removePropertyChangeListener(propertyChangeListener);
+ }
}
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 ad95c823..6a0b4b08 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
@@ -1,5 +1,8 @@
package se.leap.bitmaskclient.eip;
+import static se.leap.bitmaskclient.base.utils.TimezoneHelper.timezoneDistance;
+import static se.leap.bitmaskclient.base.utils.TimezoneHelper.getCurrentTimezone;
+
import android.util.Log;
import java.util.ArrayList;
@@ -10,9 +13,6 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.getCurrentTimezone;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.timezoneDistance;
-
public class GatewaySelector {
private final static String TAG = GatewaySelector.class.getSimpleName();
List<Gateway> gateways;
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
index b32671ae..f08371c6 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
@@ -33,6 +33,8 @@ import android.os.ParcelFileDescriptor;
import android.system.OsConstants;
import android.util.Log;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.Observable;
import java.util.Observer;
@@ -43,7 +45,7 @@ import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-public class VoidVpnService extends VpnService implements Observer, VpnNotificationManager.VpnServiceCallback {
+public class VoidVpnService extends VpnService implements PropertyChangeListener, VpnNotificationManager.VpnServiceCallback {
static final String TAG = VoidVpnService.class.getSimpleName();
private ParcelFileDescriptor fd;
@@ -183,10 +185,11 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat
getApplicationContext().startService(startEIP);
}
+
@Override
- public void update(Observable observable, Object arg) {
- if (observable instanceof EipStatus) {
- eipStatus = (EipStatus) observable;
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ eipStatus = (EipStatus) evt.getNewValue();
}
if (handlerThread.isInterrupted() || !handlerThread.isAlive()) {
return;
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
index 6d5a406e..4c8fa797 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -30,7 +30,7 @@ import static se.leap.bitmaskclient.base.models.Constants.REMOTE;
import static se.leap.bitmaskclient.base.models.Constants.TCP;
import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT;
import static se.leap.bitmaskclient.base.models.Constants.UDP;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.ObfsVpnHelper.useObfsVpn;
+import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.useObfsVpn;
import static se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient.DISPATCHER_IP;
import static se.leap.bitmaskclient.pluggableTransports.ShapeshifterClient.DISPATCHER_PORT;
diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java
index 8df1638c..c7a7040f 100644
--- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java
@@ -21,16 +21,16 @@ import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
-import java.util.Observable;
-import java.util.Observer;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.tethering.TetheringObservable;
import se.leap.bitmaskclient.tethering.TetheringState;
-import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-public class FirewallManager implements FirewallCallback, Observer {
+public class FirewallManager implements FirewallCallback, PropertyChangeListener {
public static String BITMASK_CHAIN = "bitmask_fw";
public static String BITMASK_FORWARD = "bitmask_forward";
public static String BITMASK_POSTROUTING = "bitmask_postrouting";
@@ -150,9 +150,9 @@ public class FirewallManager implements FirewallCallback, Observer {
}
@Override
- public void update(Observable o, Object arg) {
- if (o instanceof TetheringObservable) {
- TetheringObservable observable = (TetheringObservable) o;
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (TetheringObservable.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ TetheringObservable observable = (TetheringObservable) evt.getNewValue();
TetheringState state = observable.getTetheringState();
if (state.hasAnyVpnTetheringAllowed() && state.hasAnyDeviceTetheringEnabled()) {
startTethering();
diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java
index 9d5ddcf9..685349ed 100644
--- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java
+++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java
@@ -4,6 +4,8 @@ import static se.leap.bitmaskclient.base.models.Constants.KCP;
import android.util.Log;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -14,7 +16,7 @@ import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.eip.EipStatus;
-public class ObfsVpnClient implements Observer, PtClientInterface {
+public class ObfsVpnClient implements PropertyChangeListener, PtClientInterface {
public static final AtomicInteger SOCKS_PORT = new AtomicInteger(4430);
public static final String SOCKS_IP = "127.0.0.1";
@@ -114,9 +116,9 @@ public class ObfsVpnClient implements Observer, PtClientInterface {
// TODO: register observer!
@Override
- public void update(Observable observable, Object arg) {
- if (observable instanceof EipStatus) {
- EipStatus status = (EipStatus) observable;
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ EipStatus status = (EipStatus) evt.getNewValue();
if (status.getLevel() == ConnectionStatus.LEVEL_NONETWORK) {
noNetwork = true;
} else {
diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java
index 102dcf35..e57401f8 100644
--- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java
+++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ShapeshifterClient.java
@@ -21,14 +21,14 @@ import android.os.Handler;
import android.os.Looper;
import android.util.Log;
-import java.util.Observable;
-import java.util.Observer;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.eip.EipStatus;
-public class ShapeshifterClient implements Observer {
+public class ShapeshifterClient implements PropertyChangeListener {
public static final String DISPATCHER_PORT = "4430";
public static final String DISPATCHER_IP = "127.0.0.1";
@@ -123,10 +123,11 @@ public class ShapeshifterClient implements Observer {
return false;
}
+
@Override
- public void update(Observable observable, Object arg) {
- if (observable instanceof EipStatus) {
- EipStatus status = (EipStatus) observable;
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (EipStatus.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
+ EipStatus status = (EipStatus) evt.getNewValue();
if (status.getLevel() == ConnectionStatus.LEVEL_NONETWORK) {
noNetwork = true;
} else {
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;
diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java
index a25f8c85..5d62b081 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java
@@ -18,15 +18,19 @@ package se.leap.bitmaskclient.tethering;
import androidx.annotation.NonNull;
-import java.util.Observable;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
-public class TetheringObservable extends Observable {
+public class TetheringObservable {
private static TetheringObservable instance;
private TetheringState tetheringState;
+ private final PropertyChangeSupport changeSupport;
+ public static final String PROPERTY_CHANGE = "TetheringObservable";
private TetheringObservable() {
tetheringState = new TetheringState();
+ changeSupport = new PropertyChangeSupport(this);
}
public static TetheringObservable getInstance() {
@@ -36,10 +40,17 @@ public class TetheringObservable extends Observable {
return instance;
}
+ public void addObserver(PropertyChangeListener propertyChangeListener) {
+ changeSupport.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ public void deleteObserver(PropertyChangeListener propertyChangeListener) {
+ changeSupport.removePropertyChangeListener(propertyChangeListener);
+ }
+
public static void allowVpnWifiTethering(boolean enabled) {
if (getInstance().tetheringState.isVpnWifiTetheringAllowed != enabled) {
getInstance().tetheringState.isVpnWifiTetheringAllowed = enabled;
- getInstance().setChanged();
getInstance().notifyObservers();
}
}
@@ -47,7 +58,6 @@ public class TetheringObservable extends Observable {
public static void allowVpnUsbTethering(boolean enabled) {
if (getInstance().tetheringState.isVpnUsbTetheringAllowed != enabled) {
getInstance().tetheringState.isVpnUsbTetheringAllowed = enabled;
- getInstance().setChanged();
getInstance().notifyObservers();
}
}
@@ -55,7 +65,6 @@ public class TetheringObservable extends Observable {
public static void allowVpnBluetoothTethering(boolean enabled) {
if (getInstance().tetheringState.isVpnBluetoothTetheringAllowed != enabled) {
getInstance().tetheringState.isVpnBluetoothTetheringAllowed = enabled;
- getInstance().setChanged();
getInstance().notifyObservers();
}
}
@@ -70,7 +79,6 @@ public class TetheringObservable extends Observable {
state.wifiAddress = address;
state.lastSeenWifiAddress = address.isEmpty() ? state.lastSeenWifiAddress : address;
state.lastSeenWifiInterface = interfaceName.isEmpty() ? state.lastSeenWifiInterface : interfaceName;
- getInstance().setChanged();
getInstance().notifyObservers();
}
@@ -86,7 +94,6 @@ public class TetheringObservable extends Observable {
state.usbInterface = interfaceName;
state.lastSeenUsbAddress = address.isEmpty() ? state.lastSeenUsbAddress : address;
state.lastSeenUsbInterface = interfaceName.isEmpty() ? state.lastSeenUsbInterface : interfaceName;
- getInstance().setChanged();
getInstance().notifyObservers();
}
}
@@ -101,7 +108,6 @@ public class TetheringObservable extends Observable {
state.bluetoothInterface = interfaceName;
state.lastSeenBluetoothAddress = address.isEmpty() ? state.lastSeenBluetoothAddress : address;
state.lastSeenBluetoothInterface = interfaceName.isEmpty() ? state.lastSeenBluetoothInterface : interfaceName;
- getInstance().setChanged();
getInstance().notifyObservers();
}
}
@@ -121,4 +127,8 @@ public class TetheringObservable extends Observable {
public TetheringState getTetheringState() {
return tetheringState;
}
+
+ private void notifyObservers() {
+ changeSupport.firePropertyChange(PROPERTY_CHANGE, null, getInstance());
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java
index b1c4ca83..f13eb70e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java
@@ -30,6 +30,8 @@ import androidx.annotation.Nullable;
import org.torproject.jni.ClientTransportPluginInterface;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
@@ -38,8 +40,6 @@ import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashMap;
-import java.util.Observable;
-import java.util.Observer;
import java.util.Random;
import java.util.Scanner;
import java.util.Vector;
@@ -49,7 +49,7 @@ import java.util.regex.Pattern;
import IPtProxy.IPtProxy;
-public class ClientTransportPlugin implements ClientTransportPluginInterface, Observer {
+public class ClientTransportPlugin implements ClientTransportPluginInterface, PropertyChangeListener {
public static String TAG = ClientTransportPlugin.class.getSimpleName();
private HashMap<String, String> mFronts;
@@ -221,8 +221,8 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface, Ob
}
@Override
- public void update(Observable o, Object arg) {
- if (o instanceof TorStatusObservable) {
+ public void propertyChange(PropertyChangeEvent evt) {
+ if(TorStatusObservable.PROPERTY_CHANGE.equals(evt.getPropertyName())) {
TorStatusObservable.SnowflakeStatus snowflakeStatus = TorStatusObservable.getSnowflakeStatus();
if (snowflakeStatus == this.snowflakeStatus) {
return;
diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
index 8bb41dd2..b1ad6084 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
@@ -29,8 +29,8 @@ import android.util.Log;
import androidx.annotation.Nullable;
-import java.util.Observable;
-import java.util.Observer;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -39,10 +39,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
import se.leap.bitmaskclient.R;
-public class TorStatusObservable extends Observable {
+public class TorStatusObservable {
private static final String TAG = TorStatusObservable.class.getSimpleName();
+ private final PropertyChangeSupport propertyChange;
+ public static final String PROPERTY_CHANGE = "TorStatusObservable";
+
public interface StatusCondition {
boolean met();
}
@@ -98,6 +101,7 @@ public class TorStatusObservable extends Observable {
private TorStatusObservable() {
torNotificationManager = new TorNotificationManager();
+ propertyChange = new PropertyChangeSupport(this);
}
public static TorStatusObservable getInstance() {
@@ -126,11 +130,11 @@ public class TorStatusObservable extends Observable {
public static boolean waitUntil(StatusCondition condition, int timeout) throws InterruptedException, TimeoutException {
CountDownLatch countDownLatch = new CountDownLatch(1);
final AtomicBoolean conditionMet = new AtomicBoolean(false);
- Observer observer = (o, arg) -> {
- if (condition.met()) {
- countDownLatch.countDown();
- conditionMet.set(true);
- }
+ PropertyChangeListener observer = evt -> {
+ if (condition.met()) {
+ countDownLatch.countDown();
+ conditionMet.set(true);
+ }
};
if (condition.met()) {
// no need to wait
@@ -145,6 +149,14 @@ public class TorStatusObservable extends Observable {
return true;
}
+ public void addObserver(PropertyChangeListener propertyChangeListener) {
+ propertyChange.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ public void deleteObserver(PropertyChangeListener propertyChangeListener) {
+ propertyChange.removePropertyChangeListener(propertyChangeListener);
+ }
+
public static void logSnowflakeMessage(Context context, String message) {
addLog(message);
if (getInstance().status != TorStatus.OFF) {
@@ -191,7 +203,6 @@ public class TorStatusObservable extends Observable {
getInstance().lastSnowflakeLog = context.getString(R.string.snowflake_sending_data);
}
Log.d(TAG, "snowflake status " + getInstance().snowflakeStatus);
- instance.setChanged();
instance.notifyObservers();
}
@@ -230,7 +241,7 @@ public class TorStatusObservable extends Observable {
public static void updateState(Context context, String status, int bootstrapPercent, @Nullable String logKey) {
try {
- Log.d(TAG, "update tor state: " + status + " " + bootstrapPercent + " "+ logKey);
+ // Log.d(TAG, "update tor state: " + status + " " + bootstrapPercent + " "+ logKey);
getInstance().status = TorStatus.valueOf(status);
if (bootstrapPercent != -1) {
getInstance().bootstrapPercent = bootstrapPercent;
@@ -247,7 +258,7 @@ public class TorStatusObservable extends Observable {
getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getBootstrapProgress());
}
- instance.setChanged();
+
instance.notifyObservers();
} catch (IllegalStateException e) {
@@ -255,6 +266,10 @@ public class TorStatusObservable extends Observable {
}
}
+ private void notifyObservers() {
+ instance.propertyChange.firePropertyChange(PROPERTY_CHANGE, null, instance);
+ }
+
private static String getStringFor(Context context, String key) {
switch (key) {
case "conn_pt":
@@ -294,13 +309,11 @@ public class TorStatusObservable extends Observable {
public static void setLastError(String error) {
getInstance().lastError = error;
- instance.setChanged();
instance.notifyObservers();
}
public static void setProxyPort(int port) {
getInstance().port = port;
- instance.setChanged();
instance.notifyObservers();
}
@@ -345,7 +358,6 @@ public class TorStatusObservable extends Observable {
if (!getInstance().cancelled) {
getInstance().cancelled = true;
getInstance().port = -1;
- getInstance().setChanged();
getInstance().notifyObservers();
}
}