From fe6a0e47121d17d08c7d913f1db086687a569446 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 23 Jun 2021 03:27:17 +0200 Subject: initial tor-integration to circumvent blocking attempts of the provider api --- .../se/leap/bitmaskclient/base/BitmaskApp.java | 4 + .../leap/bitmaskclient/base/models/Constants.java | 1 + .../bitmaskclient/base/utils/PreferenceHelper.java | 14 ++- .../leap/bitmaskclient/eip/EipSetupObserver.java | 37 +++++- .../bitmaskclient/providersetup/ProviderAPI.java | 134 +++++++++++++++++++++ .../providersetup/ProviderAPICommand.java | 18 +-- .../providersetup/ProviderApiManagerBase.java | 72 +++++++++-- .../connectivity/OkHttpClientGenerator.java | 25 ++-- .../bitmaskclient/tor/TorNotificationManager.java | 105 ++++++++++++++++ .../bitmaskclient/tor/TorStatusObservable.java | 102 ++++++++++++++++ app/src/main/res/values/strings.xml | 5 + 11 files changed, 483 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java create mode 100644 app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java index 4b6fea72..60c28a9a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java @@ -34,6 +34,8 @@ import se.leap.bitmaskclient.eip.EipSetupObserver; import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.tethering.TetheringStateManager; import se.leap.bitmaskclient.base.utils.PRNGFixes; +import se.leap.bitmaskclient.tor.TorNotificationManager; +import se.leap.bitmaskclient.tor.TorStatusObservable; import static android.content.Intent.CATEGORY_DEFAULT; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_DOWNLOAD_SERVICE_EVENT; @@ -53,6 +55,7 @@ public class BitmaskApp extends MultiDexApplication { private RefWatcher refWatcher; private ProviderObservable providerObservable; private DownloadBroadcastReceiver downloadBroadcastReceiver; + private TorStatusObservable torStatusObservable; @Override @@ -69,6 +72,7 @@ public class BitmaskApp extends MultiDexApplication { SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); providerObservable = ProviderObservable.getInstance(); providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences)); + torStatusObservable = TorStatusObservable.getInstance(); EipSetupObserver.init(this, preferences); AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); TetheringStateManager.getInstance().init(this); 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 3edfbb3d..f627a24e 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 @@ -41,6 +41,7 @@ public interface Constants { String RESTART_ON_UPDATE = "restart_on_update"; String LAST_UPDATE_CHECK = "last_update_check"; String PREFERRED_CITY = "preferred_city"; + String USE_TOR = "use_tor"; ////////////////////////////////////////////// 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 a3d1314e..cbea2815 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 @@ -34,6 +34,7 @@ import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.base.models.Constants.SHOW_EXPERIMENTAL; import static se.leap.bitmaskclient.base.models.Constants.USE_IPv6_FIREWALL; import static se.leap.bitmaskclient.base.models.Constants.USE_PLUGGABLE_TRANSPORTS; +import static se.leap.bitmaskclient.base.models.Constants.USE_TOR; /** * Created by cyberta on 18.03.18. @@ -212,6 +213,18 @@ public class PreferenceHelper { putString(context, PREFERRED_CITY, city); } + public static Boolean useTor(SharedPreferences preferences) { + return preferences.getBoolean(USE_TOR, true); + } + + public static boolean useTor(Context context) { + return getBoolean(context, USE_TOR, true); + } + + public static void setUseTor(Context context, boolean useTor) { + putBoolean(context, USE_TOR, useTor); + } + public static JSONObject getEipDefinitionFromPreferences(SharedPreferences preferences) { JSONObject result = new JSONObject(); try { @@ -278,5 +291,4 @@ public class PreferenceHelper { SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); preferences.edit().putBoolean(key, value).apply(); } - } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java index 1ad5f7d2..45c829b6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -28,27 +28,29 @@ import android.util.Log; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.json.JSONObject; +import org.torproject.jni.TorService; import java.util.Vector; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import de.blinkt.openvpn.LaunchVPN; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.LogItem; import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.appUpdate.DownloadServiceCommand; import se.leap.bitmaskclient.base.models.Provider; -import se.leap.bitmaskclient.providersetup.ProviderAPI; -import se.leap.bitmaskclient.providersetup.ProviderAPICommand; import se.leap.bitmaskclient.base.models.ProviderObservable; -import se.leap.bitmaskclient.appUpdate.DownloadServiceCommand; import se.leap.bitmaskclient.base.utils.PreferenceHelper; +import se.leap.bitmaskclient.providersetup.ProviderAPI; +import se.leap.bitmaskclient.providersetup.ProviderAPICommand; +import se.leap.bitmaskclient.tor.TorStatusObservable; import static android.app.Activity.RESULT_CANCELED; import static android.content.Intent.CATEGORY_DEFAULT; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NOTCONNECTED; +import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.CHECK_VERSION_FILE; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_EIP_EVENT; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_PROVIDER_API_EVENT; @@ -67,7 +69,8 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOAD import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON; import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON; -import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.CHECK_VERSION_FILE; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.STOP_PROXY; +import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; /** * Created by cyberta on 05.12.18. @@ -92,6 +95,8 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT); updateIntentFilter.addAction(BROADCAST_EIP_EVENT); updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT); + updateIntentFilter.addAction(TorService.ACTION_STATUS); + updateIntentFilter.addAction(TorService.ACTION_ERROR); updateIntentFilter.addCategory(CATEGORY_DEFAULT); LocalBroadcastManager.getInstance(context.getApplicationContext()).registerReceiver(this, updateIntentFilter); instance = this; @@ -140,11 +145,30 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta case BROADCAST_PROVIDER_API_EVENT: handleProviderApiEvent(intent); break; + case TorService.ACTION_STATUS: + handleTorStatusEvent(intent); + break; + case TorService.ACTION_ERROR: + handleTorErrorEvent(intent); + break; default: break; } } + private void handleTorErrorEvent(Intent intent) { + String error = intent.getStringExtra(Intent.EXTRA_TEXT); + Log.d(TAG, "handle Tor error event: " + error); + TorStatusObservable.setLastError(error); + } + + private void handleTorStatusEvent(Intent intent) { + String status = intent.getStringExtra(TorService.EXTRA_STATUS); + Log.d(TAG, "handle Tor status event: " + status); + TorStatusObservable.updateState(context, status); + } + + private void handleProviderApiEvent(Intent intent) { int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED); Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY); @@ -324,6 +348,9 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta setupNClosestGateway.set(0); observedProfileFromVpnStatus = null; this.changingGateway.set(changingGateway); + if (TorStatusObservable.getStatus() != OFF) { + ProviderAPICommand.execute(context.getApplicationContext(), STOP_PROXY, null); + } } /** diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index 23c750a3..dcbe9636 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -17,17 +17,36 @@ package se.leap.bitmaskclient.providersetup; import android.annotation.SuppressLint; +import android.app.Notification; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.os.Build; +import android.os.IBinder; +import android.util.Log; import androidx.annotation.NonNull; import androidx.core.app.JobIntentService; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import org.torproject.jni.TorService; + +import java.io.Closeable; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator; +import se.leap.bitmaskclient.tor.TorNotificationManager; +import se.leap.bitmaskclient.tor.TorStatusObservable; import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; +import static se.leap.bitmaskclient.base.utils.ConfigHelper.ensureNotOnMainThread; +import static se.leap.bitmaskclient.tor.TorNotificationManager.TOR_SERVICE_NOTIFICATION_ID; +import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; +import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.STOPPING; /** * Implements HTTP api methods (encapsulated in {{@link ProviderApiManager}}) @@ -49,6 +68,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB final public static String TAG = ProviderAPI.class.getSimpleName(), + STOP_PROXY = "stopProxy", SET_UP_PROVIDER = "setUpProvider", UPDATE_PROVIDER_DETAILS = "updateProviderDetails", DOWNLOAD_GEOIP_JSON = "downloadGeoIpJson", @@ -85,6 +105,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB INCORRECTLY_DOWNLOADED_GEOIP_JSON = 18; ProviderApiManager providerApiManager; + private volatile TorServiceConnection torServiceConnection; //TODO: refactor me, please! //used in insecure flavor only @@ -115,14 +136,127 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB providerApiManager.handleIntent(command); } + @Override + public void onDestroy() { + super.onDestroy(); + if (torServiceConnection != null) { + torServiceConnection.close(); + torServiceConnection = null; + } + } + @Override public void broadcastEvent(Intent intent) { LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } + + + @Override + public int initTorConnection() { + initTorServiceConnection(this); + if (torServiceConnection != null) { + Intent torServiceIntent = new Intent(this, TorService.class); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Notification notification = TorNotificationManager.buildTorForegroundNotification(getApplicationContext()); + //noinspection NewApi + getApplicationContext().startForegroundService(torServiceIntent); + torServiceConnection.torService.startForeground(TOR_SERVICE_NOTIFICATION_ID, notification); + } else { + getApplicationContext().startService(torServiceIntent); + } + + return torServiceConnection.torService.getHttpTunnelPort(); + } + + return -1; + } + + @Override + public void stopTorConnection() { + if (TorStatusObservable.getStatus() != OFF) { + TorStatusObservable.updateState(this, STOPPING.toString()); + initTorServiceConnection(this); + if (torServiceConnection != null) { + torServiceConnection.torService.stopSelf(); + } + } + } + private ProviderApiManager initApiManager() { SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(getResources()); return new ProviderApiManager(preferences, getResources(), clientGenerator, this); } + + /** + * Assigns a new TorServiceConnection to ProviderAPI's member variable torServiceConnection. + * Only one thread at a time can create the service connection, that will be shared between threads + * + * @throws InterruptedException thrown if thread gets interrupted + * @throws IllegalStateException thrown if this method was not called from a background thread + */ + private void initTorServiceConnection(Context context) { + if (PreferenceHelper.useTor(context)) { + try { + if (torServiceConnection == null) { + Log.d(TAG, "serviceConnection is still null"); + torServiceConnection = new TorServiceConnection(context); + } + } catch (InterruptedException | IllegalStateException e) { + e.printStackTrace(); + } + } + } + + public static class TorServiceConnection implements Closeable { + private final Context context; + private ServiceConnection serviceConnection; + private TorService torService; + + TorServiceConnection(Context context) throws InterruptedException, IllegalStateException { + this.context = context; + ensureNotOnMainThread(context); + Log.d(TAG, "initSynchronizedServiceConnection!"); + initSynchronizedServiceConnection(context); + } + + @Override + public void close() { + context.unbindService(serviceConnection); + } + + private void initSynchronizedServiceConnection(final Context context) throws InterruptedException { + final BlockingQueue blockingQueue = new LinkedBlockingQueue<>(1); + this.serviceConnection = new ServiceConnection() { + volatile boolean mConnectedAtLeastOnce = false; + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (!mConnectedAtLeastOnce) { + mConnectedAtLeastOnce = true; + try { + TorService.LocalBinder binder = (TorService.LocalBinder) service; + blockingQueue.put(binder.getService()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + Intent intent = new Intent(context, TorService.class); + context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + torService = blockingQueue.take(); + } + + public TorService getService() { + return torService; + } + } + } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java index 1408dce8..3cdfcab0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPICommand.java @@ -13,12 +13,12 @@ import se.leap.bitmaskclient.base.models.Provider; public class ProviderAPICommand { private static final String TAG = ProviderAPICommand.class.getSimpleName(); - private Context context; + private final Context context; - private String action; - private Bundle parameters; - private ResultReceiver resultReceiver; - private Provider provider; + private final String action; + private final Bundle parameters; + private final ResultReceiver resultReceiver; + private final Provider provider; private ProviderAPICommand(@NonNull Context context, @NonNull String action, @NonNull Provider provider, ResultReceiver resultReceiver) { this(context.getApplicationContext(), action, Bundle.EMPTY, provider, resultReceiver); @@ -64,22 +64,22 @@ public class ProviderAPICommand { return command; } - public static void execute(Context context, String action, @NonNull Provider provider) { + public static void execute(Context context, String action, Provider provider) { ProviderAPICommand command = new ProviderAPICommand(context, action, provider); command.execute(); } - public static void execute(Context context, String action, Bundle parameters, @NonNull Provider provider) { + public static void execute(Context context, String action, Bundle parameters, Provider provider) { ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider); command.execute(); } - public static void execute(Context context, String action, Bundle parameters, @NonNull Provider provider, ResultReceiver resultReceiver) { + public static void execute(Context context, String action, Bundle parameters, Provider provider, ResultReceiver resultReceiver) { ProviderAPICommand command = new ProviderAPICommand(context, action, parameters, provider, resultReceiver); command.execute(); } - public static void execute(Context context, String action, @NonNull Provider provider, ResultReceiver resultReceiver) { + public static void execute(Context context, String action, Provider provider, ResultReceiver resultReceiver) { ProviderAPICommand command = new ProviderAPICommand(context, action, provider, resultReceiver); command.execute(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java index c5dc6572..8118c872 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -48,6 +48,9 @@ import java.security.interfaces.RSAPrivateKey; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; +import java.util.Observer; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; @@ -59,10 +62,13 @@ import se.leap.bitmaskclient.base.models.Constants.CREDENTIAL_ERRORS; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.models.ProviderObservable; import se.leap.bitmaskclient.base.utils.ConfigHelper; +import se.leap.bitmaskclient.base.utils.PreferenceHelper; +import se.leap.bitmaskclient.eip.EipStatus; import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator; import se.leap.bitmaskclient.providersetup.models.LeapSRPSession; import se.leap.bitmaskclient.providersetup.models.SrpCredentials; import se.leap.bitmaskclient.providersetup.models.SrpRegistrationData; +import se.leap.bitmaskclient.tor.TorStatusObservable; import static se.leap.bitmaskclient.R.string.certificate_error; import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; @@ -119,6 +125,7 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_OK; import static se.leap.bitmaskclient.providersetup.ProviderAPI.RECEIVER_KEY; import static se.leap.bitmaskclient.providersetup.ProviderAPI.SET_UP_PROVIDER; import static se.leap.bitmaskclient.providersetup.ProviderAPI.SIGN_UP; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.STOP_PROXY; import static se.leap.bitmaskclient.providersetup.ProviderAPI.SUCCESSFUL_LOGIN; import static se.leap.bitmaskclient.providersetup.ProviderAPI.SUCCESSFUL_LOGOUT; import static se.leap.bitmaskclient.providersetup.ProviderAPI.SUCCESSFUL_SIGNUP; @@ -128,6 +135,8 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE; import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING; import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON; import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE; +import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.ON; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getProxyPort; /** * Implements the logic of the http api calls. The methods of this class needs to be called from @@ -140,9 +149,11 @@ public abstract class ProviderApiManagerBase { public interface ProviderApiServiceCallback { void broadcastEvent(Intent intent); + int initTorConnection(); + void stopTorConnection(); } - private ProviderApiServiceCallback serviceCallback; + private final ProviderApiServiceCallback serviceCallback; protected SharedPreferences preferences; protected Resources resources; @@ -164,17 +175,22 @@ public abstract class ProviderApiManagerBase { String action = command.getAction(); Bundle parameters = command.getBundleExtra(PARAMETERS); - Provider provider = command.getParcelableExtra(PROVIDER_KEY); + if (action == null) { + Log.e(TAG, "Intent without action sent!"); + return; + } - if (provider == null) { + Provider provider = null; + if (command.getParcelableExtra(PROVIDER_KEY) != null) { + provider = command.getParcelableExtra(PROVIDER_KEY); + } else if (!STOP_PROXY.equals(action)) { //TODO: consider returning error back e.g. NO_PROVIDER Log.e(TAG, action +" called without provider!"); return; } - if (action == null) { - Log.e(TAG, "Intent without action sent!"); - return; - } + + // uncomment for testing --v + TorStatusObservable.setProxyPort(startTorProxy()); Bundle result = new Bundle(); switch (action) { @@ -269,9 +285,43 @@ public abstract class ProviderApiManagerBase { } ProviderObservable.getInstance().setProviderForDns(null); } + break; + case STOP_PROXY: + serviceCallback.stopTorConnection(); + break; } } + protected int startTorProxy() { + int port = -1; + if (PreferenceHelper.useTor(preferences) && EipStatus.getInstance().isDisconnected() ) { + port = serviceCallback.initTorConnection(); + if (port != -1) { + try { + waitForTorCircuits(); + } catch (InterruptedException e) { + e.printStackTrace(); + port = -1; + } + } + } + return port; + } + + private void waitForTorCircuits() throws InterruptedException { + if (TorStatusObservable.getStatus() == ON) { + return; + } + CountDownLatch countDownLatch = new CountDownLatch(1); + Observer observer = (o, arg) -> { + if (TorStatusObservable.getStatus() == ON) { + countDownLatch.countDown(); + } + }; + TorStatusObservable.getInstance().addObserver(observer); + countDownLatch.await(90, TimeUnit.SECONDS); + } + void resetProviderDetails(Provider provider) { provider.reset(); deleteProviderDetailsFromPreferences(preferences, provider.getDomain()); @@ -342,7 +392,7 @@ public abstract class ProviderApiManagerBase { private Bundle register(Provider provider, String username, String password) { JSONObject stepResult = null; - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), stepResult); if (okHttpClient == null) { return backendErrorNotification(stepResult, username); } @@ -401,7 +451,7 @@ public abstract class ProviderApiManagerBase { String providerApiUrl = provider.getApiUrlWithVersion(); - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), stepResult); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), stepResult); if (okHttpClient == null) { return backendErrorNotification(stepResult, username); } @@ -681,7 +731,7 @@ public abstract class ProviderApiManagerBase { JSONObject errorJson = new JSONObject(); String providerUrl = provider.getApiUrlString() + "/provider.json"; - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), errorJson); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), errorJson); if (okHttpClient == null) { result.putString(ERRORS, errorJson.toString()); return false; @@ -950,7 +1000,7 @@ public abstract class ProviderApiManagerBase { } private boolean logOut(Provider provider) { - OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), new JSONObject()); + OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), new JSONObject()); if (okHttpClient == null) { return false; } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java index 2077a8b9..ea619263 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java @@ -18,6 +18,7 @@ package se.leap.bitmaskclient.providersetup.connectivity; import android.content.res.Resources; +import android.net.LocalSocketAddress; import android.os.Build; import androidx.annotation.NonNull; @@ -26,6 +27,9 @@ import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.SocketAddress; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.KeyStoreException; @@ -50,6 +54,7 @@ import static se.leap.bitmaskclient.R.string.certificate_error; import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message; import static se.leap.bitmaskclient.R.string.keyChainAccessError; +import static se.leap.bitmaskclient.R.string.proxy; import static se.leap.bitmaskclient.R.string.server_unreachable_message; import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS; import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString; @@ -61,34 +66,35 @@ import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormatted public class OkHttpClientGenerator { Resources resources; + private final static String PROXY_HOST = "127.0.0.1"; public OkHttpClientGenerator(/*SharedPreferences preferences,*/ Resources resources) { this.resources = resources; } - public OkHttpClient initCommercialCAHttpClient(JSONObject initError) { - return initHttpClient(initError, null); + public OkHttpClient initCommercialCAHttpClient(JSONObject initError, int proxyPort) { + return initHttpClient(initError, null, proxyPort); } - public OkHttpClient initSelfSignedCAHttpClient(String caCert, JSONObject initError) { - return initHttpClient(initError, caCert); + public OkHttpClient initSelfSignedCAHttpClient(String caCert, int proxyPort, JSONObject initError) { + return initHttpClient(initError, caCert, proxyPort); } public OkHttpClient init() { try { - return createClient(null); + return createClient(null, -1); } catch (Exception e) { e.printStackTrace(); } return null; } - private OkHttpClient initHttpClient(JSONObject initError, String certificate) { + private OkHttpClient initHttpClient(JSONObject initError, String certificate, int proxyPort) { if (resources == null) { return null; } try { - return createClient(certificate); + return createClient(certificate, proxyPort); } catch (IllegalArgumentException e) { e.printStackTrace(); // TODO ca cert is invalid - show better error ?! @@ -117,7 +123,7 @@ public class OkHttpClientGenerator { return null; } - private OkHttpClient createClient(String certificate) throws Exception { + private OkHttpClient createClient(String certificate, int proxyPort) throws Exception { TLSCompatSocketFactory sslCompatFactory; ConnectionSpec spec = getConnectionSpec(); OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); @@ -131,6 +137,9 @@ public class OkHttpClientGenerator { clientBuilder.cookieJar(getCookieJar()) .connectionSpecs(Collections.singletonList(spec)); clientBuilder.dns(new DnsResolver()); + if (proxyPort != -1) { + clientBuilder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, proxyPort))); + } return clientBuilder.build(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java new file mode 100644 index 00000000..71a2735c --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java @@ -0,0 +1,105 @@ +package se.leap.bitmaskclient.tor; +/** + * Copyright (c) 2021 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import android.annotation.TargetApi; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; + +import se.leap.bitmaskclient.R; + +public class TorNotificationManager { + public final static int TOR_SERVICE_NOTIFICATION_ID = 10; + static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "bitmask_tor_service_news"; + + + public TorNotificationManager() {} + + + public static Notification buildTorForegroundNotification(Context context) { + NotificationManager notificationManager = initNotificationManager(context); + if (notificationManager == null) { + return null; + } + NotificationCompat.Builder notificationBuilder = initNotificationBuilderDefaults(context); + return notificationBuilder + .setSmallIcon(R.drawable.ic_bridge_36) + .setWhen(System.currentTimeMillis()) + .setContentTitle("Using Bridges to configure provider.").build(); + } + + public void buildTorNotification(Context context, String state) { + NotificationManager notificationManager = initNotificationManager(context); + if (notificationManager == null) { + return; + } + NotificationCompat.Builder notificationBuilder = initNotificationBuilderDefaults(context); + notificationBuilder + .setSmallIcon(R.drawable.ic_bridge_36) + .setWhen(System.currentTimeMillis()) + .setTicker(state) + .setContentTitle(context.getString(R.string.tor_provider_setup)) + .setContentText(state); + notificationManager.notify(TOR_SERVICE_NOTIFICATION_ID, notificationBuilder.build()); + } + + + private static NotificationManager initNotificationManager(Context context) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return null; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(notificationManager); + } + return notificationManager; + } + + @TargetApi(26) + private static void createNotificationChannel(NotificationManager notificationManager) { + CharSequence name = "Bitmask Tor Service"; + String description = "Informs about usage of bridges to configure Bitmask."; + NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_NEWSTATUS_ID, + name, + NotificationManager.IMPORTANCE_LOW); + channel.setSound(null, null); + channel.setDescription(description); + notificationManager.createNotificationChannel(channel); + } + + private static NotificationCompat.Builder initNotificationBuilderDefaults(Context context) { + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_NEWSTATUS_ID); + notificationBuilder. + setDefaults(Notification.DEFAULT_ALL). + setAutoCancel(true); + return notificationBuilder; + } + + public void cancelNotifications(Context context) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return; + } + notificationManager.cancel(TOR_SERVICE_NOTIFICATION_ID); + } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java new file mode 100644 index 00000000..ed4ae24b --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java @@ -0,0 +1,102 @@ +package se.leap.bitmaskclient.tor; + +import android.content.Context; +import android.os.Handler; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Observable; + +import se.leap.bitmaskclient.R; + +public class TorStatusObservable extends Observable { + + private static final String TAG = TorStatusObservable.class.getSimpleName(); + + public enum TorStatus { + ON, + OFF, + STARTING, + STOPPING, + UNKOWN + } + + private static TorStatusObservable instance; + private TorStatus status = TorStatus.UNKOWN; + private final TorNotificationManager torNotificationManager; + private String lastError; + private int port = -1; + + private TorStatusObservable() { + torNotificationManager = new TorNotificationManager(); + } + + public static TorStatusObservable getInstance() { + if (instance == null) { + instance = new TorStatusObservable(); + } + return instance; + } + + public static TorStatus getStatus() { + return getInstance().status; + } + + + public static void updateState(Context context, String status) { + try { + Log.d(TAG, "update tor state: " + status); + getInstance().status = TorStatus.valueOf(status); + if (getInstance().status == TorStatus.OFF) { + getInstance().torNotificationManager.cancelNotifications(context); + } else { + getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context)); + } + instance.setChanged(); + instance.notifyObservers(); + + + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + 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(); + } + + public static int getProxyPort() { + return getInstance().port; + } + + + @Nullable + public String getLastError() { + return lastError; + } + + private static String getStringForCurrentStatus(Context context) { + switch (getInstance().status) { + case ON: + return context.getString(R.string.tor_started); + case STARTING: + return context.getString(R.string.tor_starting); + case STOPPING: + return context.getString(R.string.tor_stopping); + case OFF: + case UNKOWN: + break; + } + return null; + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e883b974..b43a0683 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,4 +155,9 @@ Automatic Your traffic is currently routed through: + Starting Tor with bridges. + Stopping Tor with bridges. + Running Tor with bridges to fetch provider configuration. + Using Bridges to configure provider. + -- cgit v1.2.3 From a28080ddfe35ac85c7da552739dc27f0687734a2 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 23 Jun 2021 03:36:40 +0200 Subject: don't use hard coded strings --- app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java index 71a2735c..4acc2b7e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java @@ -45,7 +45,7 @@ public class TorNotificationManager { return notificationBuilder .setSmallIcon(R.drawable.ic_bridge_36) .setWhen(System.currentTimeMillis()) - .setContentTitle("Using Bridges to configure provider.").build(); + .setContentTitle(context.getString(R.string.tor_provider_setup)).build(); } public void buildTorNotification(Context context, String state) { -- cgit v1.2.3 From 7325a74661e943fc0f989ef5e230f0862cc8e9fb Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 23 Jun 2021 16:33:54 +0200 Subject: automatically start VPN after switching provider --- app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java | 1 + 1 file changed, 1 insertion(+) (limited to 'app/src/main') 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 126c4a98..99f6826f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java @@ -202,6 +202,7 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener, switch (requestCode) { case REQUEST_CODE_SWITCH_PROVIDER: EipCommand.stopVPN(this.getApplicationContext()); + EipCommand.startVPN(this.getApplicationContext(), false); break; case REQUEST_CODE_CONFIGURE_LEAP: Log.d(TAG, "REQUEST_CODE_CONFIGURE_LEAP - onActivityResult - MainActivity"); -- cgit v1.2.3 From 57915c157c880f29ac9577d1074bf2d54156231f Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 23 Jun 2021 16:35:38 +0200 Subject: automatically close navigation drawer after switch account item was selected --- .../leap/bitmaskclient/base/fragments/NavigationDrawerFragment.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'app/src/main') 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 5cae1591..f905a7d2 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 @@ -289,8 +289,10 @@ public class NavigationDrawerFragment extends Fragment implements SharedPreferen if (isDefaultBitmask()) { IconTextEntry switchProvider = drawerView.findViewById(R.id.switch_provider); switchProvider.setVisibility(VISIBLE); - switchProvider.setOnClickListener(v -> - getActivity().startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER)); + switchProvider.setOnClickListener(v -> { + closeDrawer(); + getActivity().startActivityForResult(new Intent(getActivity(), ProviderListActivity.class), REQUEST_CODE_SWITCH_PROVIDER); + }); } } -- cgit v1.2.3 From fca60f51acf8eea48fc4086db00cba1097d097b7 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 24 Jun 2021 23:12:45 +0200 Subject: tweak wording --- app/src/main/res/values/strings.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b43a0683..7c0e6334 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,9 +155,8 @@ Automatic Your traffic is currently routed through: - Starting Tor with bridges. - Stopping Tor with bridges. - Running Tor with bridges to fetch provider configuration. - Using Bridges to configure provider. + Starting bridges for censorship circumvention. Please hold on… + Stopping bridges. + Using bridges for censorship circumvention. -- cgit v1.2.3 From 236c17ec3f4348a9f0d4ec4a2454b9fbfaf8707f Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 24 Jun 2021 23:15:41 +0200 Subject: show tor status info in provider setup activies --- .../activities/ConfigWizardBaseActivity.java | 35 ++++++++++++++++++---- .../providersetup/activities/LoginActivity.java | 2 +- .../providersetup/activities/SignupActivity.java | 2 +- .../bitmaskclient/tor/TorNotificationManager.java | 7 +++-- .../bitmaskclient/tor/TorStatusObservable.java | 2 +- .../main/res/layout-xlarge/v_loading_screen.xml | 22 ++++++++++++-- app/src/main/res/layout/v_add_provider.xml | 20 +++++++++++-- app/src/main/res/layout/v_loading_screen.xml | 22 ++++++++++++-- 8 files changed, 94 insertions(+), 18 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java index b2f13e07..7d452200 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java @@ -19,11 +19,14 @@ import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.Guideline; import androidx.core.content.ContextCompat; +import java.util.Observable; +import java.util.Observer; + import butterknife.BindView; -import butterknife.Optional; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.base.views.ProviderHeaderView; +import se.leap.bitmaskclient.tor.TorStatusObservable; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.View.GONE; @@ -37,7 +40,7 @@ import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; * Created by fupduck on 09.01.18. */ -public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { +public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity implements Observer { private static final String TAG = ConfigWizardBaseActivity.class.getName(); public static final float GUIDE_LINE_COMPACT_DELTA = 0.1f; @@ -55,9 +58,13 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { @BindView(R.id.progressbar) protected ProgressBar progressBar; + @Nullable + @BindView(R.id.progressbar_title) + protected AppCompatTextView progressbarTitle; + @Nullable @BindView(R.id.progressbar_description) - protected AppCompatTextView progressbarText; + protected AppCompatTextView progressbarDescription; //Only tablet layouts have guidelines as they are based on a ConstraintLayout @Nullable @@ -142,12 +149,15 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { protected void onPause() { super.onPause(); isActivityShowing = false; + TorStatusObservable.getInstance().deleteObserver(this); } @Override protected void onResume() { super.onResume(); isActivityShowing = true; + TorStatusObservable.getInstance().addObserver(this); + setProgressbarDescription(TorStatusObservable.getStringForCurrentStatus(this)); } protected void restoreState(Bundle savedInstanceState) { @@ -184,11 +194,18 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { loadingScreen.setVisibility(VISIBLE); } - protected void setProgressbarText(@StringRes int progressbarText) { - if (this.progressbarText == null) { + protected void setProgressbarTitle(@StringRes int progressbarTitle) { + if (this.progressbarTitle == null) { + return; + } + this.progressbarTitle.setText(progressbarTitle); + } + + protected void setProgressbarDescription(String progressbarDescription) { + if (this.progressbarDescription == null) { return; } - this.progressbarText.setText(progressbarText); + this.progressbarDescription.setText(progressbarDescription); } @@ -287,4 +304,10 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity { }); } + @Override + public void update(Observable o, Object arg) { + if (o instanceof TorStatusObservable) { + runOnUiThread(() -> setProgressbarDescription(TorStatusObservable.getStringForCurrentStatus(ConfigWizardBaseActivity.this))); + } + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java index a8bac6d8..9a5f31f2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/LoginActivity.java @@ -17,7 +17,7 @@ public class LoginActivity extends ProviderCredentialsBaseActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setProgressbarText(R.string.logging_in); + setProgressbarTitle(R.string.logging_in); setProviderHeaderLogo(R.drawable.logo); setProviderHeaderText(R.string.login_to_profile); } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java index c0245845..16007a70 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/SignupActivity.java @@ -37,7 +37,7 @@ public class SignupActivity extends ProviderCredentialsBaseActivity { setProviderHeaderLogo(R.drawable.logo); setProviderHeaderText(R.string.create_profile); - setProgressbarText(R.string.signing_up); + setProgressbarTitle(R.string.signing_up); setButtonText(R.string.signup_button); passwordVerificationField.setVisibility(VISIBLE); diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java index 4acc2b7e..71b5c378 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java @@ -27,6 +27,8 @@ import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.base.models.Provider; +import se.leap.bitmaskclient.base.models.ProviderObservable; public class TorNotificationManager { public final static int TOR_SERVICE_NOTIFICATION_ID = 10; @@ -45,7 +47,7 @@ public class TorNotificationManager { return notificationBuilder .setSmallIcon(R.drawable.ic_bridge_36) .setWhen(System.currentTimeMillis()) - .setContentTitle(context.getString(R.string.tor_provider_setup)).build(); + .setContentTitle(context.getString(R.string.tor_started)).build(); } public void buildTorNotification(Context context, String state) { @@ -58,8 +60,7 @@ public class TorNotificationManager { .setSmallIcon(R.drawable.ic_bridge_36) .setWhen(System.currentTimeMillis()) .setTicker(state) - .setContentTitle(context.getString(R.string.tor_provider_setup)) - .setContentText(state); + .setContentTitle(state); notificationManager.notify(TOR_SERVICE_NOTIFICATION_ID, notificationBuilder.build()); } 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 ed4ae24b..e806e441 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java @@ -85,7 +85,7 @@ public class TorStatusObservable extends Observable { return lastError; } - private static String getStringForCurrentStatus(Context context) { + public static String getStringForCurrentStatus(Context context) { switch (getInstance().status) { case ON: return context.getString(R.string.tor_started); diff --git a/app/src/main/res/layout-xlarge/v_loading_screen.xml b/app/src/main/res/layout-xlarge/v_loading_screen.xml index a002665f..adcbfabc 100644 --- a/app/src/main/res/layout-xlarge/v_loading_screen.xml +++ b/app/src/main/res/layout-xlarge/v_loading_screen.xml @@ -1,11 +1,14 @@ + android:visibility="gone" + tools:visibility="visible" + > + + + android:visibility="gone" + tools:visibility="visible"> + + + android:visibility="gone" + tools:visibility="visible" + > + + Date: Thu, 24 Jun 2021 23:22:00 +0200 Subject: Stop tor proxy if provider api communication failed. Proxy will be reinitiated on next api call --- .../main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java index 45c829b6..f42a7d3d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -68,7 +68,11 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON; import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_NOK; import static se.leap.bitmaskclient.providersetup.ProviderAPI.STOP_PROXY; import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; @@ -202,6 +206,13 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta case INCORRECTLY_DOWNLOADED_GEOIP_JSON: maybeStartEipService(resultData); break; + case PROVIDER_NOK: + case INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE: + case INCORRECTLY_DOWNLOADED_EIP_SERVICE: + case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: + if (TorStatusObservable.getStatus() != OFF) { + ProviderAPICommand.execute(context.getApplicationContext(), STOP_PROXY, null); + } default: break; } -- cgit v1.2.3 From b22698c2c4933f43e58379693f615bda235cb040 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 24 Jun 2021 23:27:21 +0200 Subject: let the tor state description in provider setup activities grow up to 2 lines --- app/src/main/res/layout-xlarge/v_loading_screen.xml | 2 +- app/src/main/res/layout/v_add_provider.xml | 2 +- app/src/main/res/layout/v_loading_screen.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/res/layout-xlarge/v_loading_screen.xml b/app/src/main/res/layout-xlarge/v_loading_screen.xml index adcbfabc..ed25f07b 100644 --- a/app/src/main/res/layout-xlarge/v_loading_screen.xml +++ b/app/src/main/res/layout-xlarge/v_loading_screen.xml @@ -37,7 +37,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:fadingEdge="horizontal" - android:singleLine="true" + android:maxLines="2" android:text="@string/configuring_provider" android:textAppearance="@style/Base.TextAppearance.AppCompat.Small" android:layout_marginTop="@dimen/standard_margin" diff --git a/app/src/main/res/layout/v_add_provider.xml b/app/src/main/res/layout/v_add_provider.xml index 1448e7e7..933f19d0 100644 --- a/app/src/main/res/layout/v_add_provider.xml +++ b/app/src/main/res/layout/v_add_provider.xml @@ -36,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:fadingEdge="horizontal" - android:singleLine="true" + android:maxLines="2" android:text="@string/configuring_provider" android:textAppearance="@style/Base.TextAppearance.AppCompat.Small" android:layout_marginTop="@dimen/standard_margin" diff --git a/app/src/main/res/layout/v_loading_screen.xml b/app/src/main/res/layout/v_loading_screen.xml index 9bda063d..f7f58e7b 100644 --- a/app/src/main/res/layout/v_loading_screen.xml +++ b/app/src/main/res/layout/v_loading_screen.xml @@ -38,7 +38,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:fadingEdge="horizontal" - android:singleLine="true" + android:maxLines="2" android:text="@string/configuring_provider" android:textAppearance="@style/Base.TextAppearance.AppCompat.Small" android:layout_marginTop="@dimen/standard_margin" -- cgit v1.2.3 From 9687a96f2608be215c6ddcc22f2ecfe6a889093a Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 24 Jun 2021 23:57:48 +0200 Subject: use notifications contentText instead of contentTitle, so that the complete tor notification text can be shown --- .../main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java index 71b5c378..a2401732 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java @@ -47,7 +47,7 @@ public class TorNotificationManager { return notificationBuilder .setSmallIcon(R.drawable.ic_bridge_36) .setWhen(System.currentTimeMillis()) - .setContentTitle(context.getString(R.string.tor_started)).build(); + .setContentText(context.getString(R.string.tor_started)).build(); } public void buildTorNotification(Context context, String state) { @@ -60,7 +60,7 @@ public class TorNotificationManager { .setSmallIcon(R.drawable.ic_bridge_36) .setWhen(System.currentTimeMillis()) .setTicker(state) - .setContentTitle(state); + .setContentText(state); notificationManager.notify(TOR_SERVICE_NOTIFICATION_ID, notificationBuilder.build()); } -- cgit v1.2.3 From aadae76e5641684cf854b6193f3015be0f7a1b0b Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 10 Jul 2021 03:39:10 +0200 Subject: update tor-android --- .../leap/bitmaskclient/tor/IPtProxyInterface.java | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyInterface.java (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyInterface.java b/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyInterface.java new file mode 100644 index 00000000..0203e082 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyInterface.java @@ -0,0 +1,72 @@ +package se.leap.bitmaskclient.tor; + +public interface IPtProxyInterface { + + /** + * StateLocation - Override TOR_PT_STATE_LOCATION, which defaults to "$TMPDIR/pt_state". + */ + void setStateLocation(String v); + + /** + * StateLocation - Override TOR_PT_STATE_LOCATION, which defaults to "$TMPDIR/pt_state". + */ + String getStateLocation(); + + /** + * SnowflakePort - Port where Snowflake will provide its service. + Only use this property after calling StartSnowflake! It might have changed after that! + */ + long snowflakePort(); + + /** + * StartSnowflake - Start the Snowflake client. + + @param ice Comma-separated list of ICE servers. + + @param url URL of signaling broker. + + @param front Front domain. + + @param logFile Name of log file. OPTIONAL + + @param logToStateDir Resolve the log file relative to Tor's PT state dir. + + @param keepLocalAddresses Keep local LAN address ICE candidates. + + @param unsafeLogging Prevent logs from being scrubbed. + + @param maxPeers Capacity for number of multiplexed WebRTC peers. DEFAULTs to 1 if less than that. + + @return Port number where Snowflake will listen on, if no error happens during start up. + */ + long startSnowflake(String ice, String url, String front, String logFile, boolean logToStateDir, boolean keepLocalAddresses, boolean unsafeLogging, long maxPeers); + + /** + * StartSnowflakeProxy - Start the Snowflake proxy. + + @param capacity Maximum concurrent clients. OPTIONAL. Defaults to 10, if 0. + + @param broker Broker URL. OPTIONAL. Defaults to https://snowflake-broker.bamsoftware.com/, if empty. + + @param relay WebSocket relay URL. OPTIONAL. Defaults to wss://snowflake.bamsoftware.com/, if empty. + + @param stun STUN URL. OPTIONAL. Defaults to stun:stun.stunprotocol.org:3478, if empty. + + @param logFile Name of log file. OPTIONAL + + @param keepLocalAddresses Keep local LAN address ICE candidates. + + @param unsafeLogging Prevent logs from being scrubbed. + */ + void startSnowflakeProxy(long capacity, String broker, String relay, String stun, String logFile, boolean keepLocalAddresses, boolean unsafeLogging); + + /** + * StopSnowflake - Stop the Snowflake client. + */ + void stopSnowflake(); + + /** + * StopSnowflakeProxy - Stop the Snowflake proxy. + */ + void stopSnowflakeProxy(); +} -- cgit v1.2.3 From 6a3b5094cbc347bc9119214f8ea074ce53452cdb Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sun, 11 Jul 2021 16:31:30 +0200 Subject: inject IPtProxy in TorService --- .../bitmaskclient/providersetup/ProviderAPI.java | 2 ++ .../se/leap/bitmaskclient/tor/IPtProxyWrapper.java | 40 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyWrapper.java (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index dcbe9636..9ba64b31 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -39,6 +39,7 @@ import java.util.concurrent.LinkedBlockingQueue; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator; +import se.leap.bitmaskclient.tor.IPtProxyWrapper; import se.leap.bitmaskclient.tor.TorNotificationManager; import se.leap.bitmaskclient.tor.TorStatusObservable; @@ -202,6 +203,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB try { if (torServiceConnection == null) { Log.d(TAG, "serviceConnection is still null"); + TorService.setIPtProxy(new IPtProxyWrapper()); torServiceConnection = new TorServiceConnection(context); } } catch (InterruptedException | IllegalStateException e) { diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyWrapper.java b/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyWrapper.java new file mode 100644 index 00000000..c7ca165e --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyWrapper.java @@ -0,0 +1,40 @@ +package se.leap.bitmaskclient.tor; + +import IPtProxy.IPtProxy; + +public class IPtProxyWrapper implements IPtProxyInterface { + @Override + public void setStateLocation(String location) { + IPtProxy.setStateLocation(location); + } + + @Override + public String getStateLocation() { + return IPtProxy.getStateLocation(); + } + + @Override + public long snowflakePort() { + return IPtProxy.snowflakePort(); + } + + @Override + public long startSnowflake(String ice, String url, String front, String logFile, boolean logToStateDir, boolean keepLocalAddresses, boolean unsafeLogging, long maxPeers) { + return IPtProxy.startSnowflake(ice, url, front, logFile, logToStateDir, keepLocalAddresses, unsafeLogging, maxPeers); + } + + @Override + public void startSnowflakeProxy(long capacity, String broker, String relay, String stun, String logFile, boolean keepLocalAddresses, boolean unsafeLogging) { + IPtProxy.startSnowflakeProxy(capacity, broker, relay, stun, logFile, keepLocalAddresses, unsafeLogging); + } + + @Override + public void stopSnowflake() { + IPtProxy.stopSnowflake(); + } + + @Override + public void stopSnowflakeProxy() { + IPtProxy.stopSnowflakeProxy(); + } +} -- cgit v1.2.3 From 02d09ff48aed5374e331f2609d27879cd801b9c3 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sun, 11 Jul 2021 19:16:39 +0200 Subject: update tor-android --- app/src/main/assets/fronts | 6 ++++++ .../main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 app/src/main/assets/fronts create mode 100644 app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java (limited to 'app/src/main') diff --git a/app/src/main/assets/fronts b/app/src/main/assets/fronts new file mode 100644 index 00000000..937332df --- /dev/null +++ b/app/src/main/assets/fronts @@ -0,0 +1,6 @@ +snowflake-target https://snowflake-broker.torproject.net.global.prod.fastly.net/ +snowflake-front cdn.sstatic.net +snowflake-stun stun:stun.stunprotocol.org:3478 +moat-cdn https://d50gd378qj74g.cloudfront.net/ +moat-url https://moat.torproject.org.global.prod.fastly.net/ +moat-front cdn.sstatic.net diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java new file mode 100644 index 00000000..9ed805e4 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java @@ -0,0 +1,4 @@ +package se.leap.bitmaskclient.tor; + +public class ClientTransportPlugin { +} -- cgit v1.2.3 From 8dc39ff8e4684cbd9235824c58a5deb70fb818ee Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sun, 11 Jul 2021 19:25:50 +0200 Subject: implement ClientTransportPluginInterface to start / stop snowflake client for tor --- .../bitmaskclient/providersetup/ProviderAPI.java | 4 +- .../bitmaskclient/tor/ClientTransportPlugin.java | 93 +++++++++++++++++++++- .../leap/bitmaskclient/tor/IPtProxyInterface.java | 72 ----------------- .../se/leap/bitmaskclient/tor/IPtProxyWrapper.java | 40 ---------- 4 files changed, 94 insertions(+), 115 deletions(-) delete mode 100644 app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyInterface.java delete mode 100644 app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyWrapper.java (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index 9ba64b31..f2376568 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -39,7 +39,7 @@ import java.util.concurrent.LinkedBlockingQueue; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator; -import se.leap.bitmaskclient.tor.IPtProxyWrapper; +import se.leap.bitmaskclient.tor.ClientTransportPlugin; import se.leap.bitmaskclient.tor.TorNotificationManager; import se.leap.bitmaskclient.tor.TorStatusObservable; @@ -203,7 +203,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB try { if (torServiceConnection == null) { Log.d(TAG, "serviceConnection is still null"); - TorService.setIPtProxy(new IPtProxyWrapper()); + TorService.setClientTransportPlugin(new ClientTransportPlugin(context.getApplicationContext())); torServiceConnection = new TorServiceConnection(context); } } catch (InterruptedException | IllegalStateException e) { 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 9ed805e4..9e3828de 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java @@ -1,4 +1,95 @@ package se.leap.bitmaskclient.tor; -public class ClientTransportPlugin { +import android.content.Context; +import android.util.Log; + +import androidx.annotation.Nullable; + +import org.torproject.jni.ClientTransportPluginInterface; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.ref.WeakReference; +import java.util.HashMap; + +import IPtProxy.IPtProxy; + +public class ClientTransportPlugin implements ClientTransportPluginInterface { + public static String TAG = ClientTransportPlugin.class.getSimpleName(); + + private HashMap mFronts; + private final WeakReference contextRef; + private long snowflakePort = -1; + + public ClientTransportPlugin(Context context) { + this.contextRef = new WeakReference<>(context); + loadCdnFronts(context); + } + + @Override + public void start() { + Context context = contextRef.get(); + if (context == null) { + return; + } + File logfile = new File(context.getApplicationContext().getCacheDir(), "snowflake.log"); + Log.d(TAG, "logfile at " + logfile.getAbsolutePath()); + if (!logfile.exists()) { + try { + logfile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + //this is using the current, default Tor snowflake infrastructure + String target = getCdnFront("snowflake-target"); + String front = getCdnFront("snowflake-front"); + String stunServer = getCdnFront("snowflake-stun"); + Log.d(TAG, "startSnowflake. target: " + target + ", front:" + front + ", stunServer" + stunServer); + snowflakePort = IPtProxy.startSnowflake( stunServer, target, front, logfile.getAbsolutePath(), false, false, true, 1); + Log.d(TAG, "startSnowflake running on port: " + snowflakePort); + } + + @Override + public void stop() { + IPtProxy.stopSnowflake(); + snowflakePort = -1; + } + + @Override + public String getTorrc() { + return "UseBridges 1\n" + + "ClientTransportPlugin snowflake socks5 127.0.0.1:" + snowflakePort + "\n" + + "Bridge snowflake 192.0.2.3:1"; + } + + private void loadCdnFronts(Context context) { + if (mFronts == null) { + mFronts = new HashMap<>(); + } + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(context.getAssets().open("fronts"))); + String line; + while (true) { + line = reader.readLine(); + if (line == null) break; + String[] front = line.split(" "); + mFronts.put(front[0], front[1]); + Log.d(TAG, "front: " + front[0] + ", " + front[1]); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Nullable + private String getCdnFront(String service) { + if (mFronts != null) { + return mFronts.get(service); + } + return null; + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyInterface.java b/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyInterface.java deleted file mode 100644 index 0203e082..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyInterface.java +++ /dev/null @@ -1,72 +0,0 @@ -package se.leap.bitmaskclient.tor; - -public interface IPtProxyInterface { - - /** - * StateLocation - Override TOR_PT_STATE_LOCATION, which defaults to "$TMPDIR/pt_state". - */ - void setStateLocation(String v); - - /** - * StateLocation - Override TOR_PT_STATE_LOCATION, which defaults to "$TMPDIR/pt_state". - */ - String getStateLocation(); - - /** - * SnowflakePort - Port where Snowflake will provide its service. - Only use this property after calling StartSnowflake! It might have changed after that! - */ - long snowflakePort(); - - /** - * StartSnowflake - Start the Snowflake client. - - @param ice Comma-separated list of ICE servers. - - @param url URL of signaling broker. - - @param front Front domain. - - @param logFile Name of log file. OPTIONAL - - @param logToStateDir Resolve the log file relative to Tor's PT state dir. - - @param keepLocalAddresses Keep local LAN address ICE candidates. - - @param unsafeLogging Prevent logs from being scrubbed. - - @param maxPeers Capacity for number of multiplexed WebRTC peers. DEFAULTs to 1 if less than that. - - @return Port number where Snowflake will listen on, if no error happens during start up. - */ - long startSnowflake(String ice, String url, String front, String logFile, boolean logToStateDir, boolean keepLocalAddresses, boolean unsafeLogging, long maxPeers); - - /** - * StartSnowflakeProxy - Start the Snowflake proxy. - - @param capacity Maximum concurrent clients. OPTIONAL. Defaults to 10, if 0. - - @param broker Broker URL. OPTIONAL. Defaults to https://snowflake-broker.bamsoftware.com/, if empty. - - @param relay WebSocket relay URL. OPTIONAL. Defaults to wss://snowflake.bamsoftware.com/, if empty. - - @param stun STUN URL. OPTIONAL. Defaults to stun:stun.stunprotocol.org:3478, if empty. - - @param logFile Name of log file. OPTIONAL - - @param keepLocalAddresses Keep local LAN address ICE candidates. - - @param unsafeLogging Prevent logs from being scrubbed. - */ - void startSnowflakeProxy(long capacity, String broker, String relay, String stun, String logFile, boolean keepLocalAddresses, boolean unsafeLogging); - - /** - * StopSnowflake - Stop the Snowflake client. - */ - void stopSnowflake(); - - /** - * StopSnowflakeProxy - Stop the Snowflake proxy. - */ - void stopSnowflakeProxy(); -} diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyWrapper.java b/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyWrapper.java deleted file mode 100644 index c7ca165e..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/tor/IPtProxyWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package se.leap.bitmaskclient.tor; - -import IPtProxy.IPtProxy; - -public class IPtProxyWrapper implements IPtProxyInterface { - @Override - public void setStateLocation(String location) { - IPtProxy.setStateLocation(location); - } - - @Override - public String getStateLocation() { - return IPtProxy.getStateLocation(); - } - - @Override - public long snowflakePort() { - return IPtProxy.snowflakePort(); - } - - @Override - public long startSnowflake(String ice, String url, String front, String logFile, boolean logToStateDir, boolean keepLocalAddresses, boolean unsafeLogging, long maxPeers) { - return IPtProxy.startSnowflake(ice, url, front, logFile, logToStateDir, keepLocalAddresses, unsafeLogging, maxPeers); - } - - @Override - public void startSnowflakeProxy(long capacity, String broker, String relay, String stun, String logFile, boolean keepLocalAddresses, boolean unsafeLogging) { - IPtProxy.startSnowflakeProxy(capacity, broker, relay, stun, logFile, keepLocalAddresses, unsafeLogging); - } - - @Override - public void stopSnowflake() { - IPtProxy.stopSnowflake(); - } - - @Override - public void stopSnowflakeProxy() { - IPtProxy.stopSnowflakeProxy(); - } -} -- cgit v1.2.3 From 0177f1ac3c24a45f4df928f962817e8a2e36ee36 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 12 Jul 2021 17:54:58 +0200 Subject: move assets folder to right directory --- app/src/main/assets/fronts | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 app/src/main/assets/fronts (limited to 'app/src/main') diff --git a/app/src/main/assets/fronts b/app/src/main/assets/fronts deleted file mode 100644 index 937332df..00000000 --- a/app/src/main/assets/fronts +++ /dev/null @@ -1,6 +0,0 @@ -snowflake-target https://snowflake-broker.torproject.net.global.prod.fastly.net/ -snowflake-front cdn.sstatic.net -snowflake-stun stun:stun.stunprotocol.org:3478 -moat-cdn https://d50gd378qj74g.cloudfront.net/ -moat-url https://moat.torproject.org.global.prod.fastly.net/ -moat-front cdn.sstatic.net -- cgit v1.2.3 From 3d9e76779f102b5cbbb29a2215415ab1a7f96aed Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 14 Jul 2021 23:53:26 +0200 Subject: stop torService immediately instead of queueing it in provider api service --- .../se/leap/bitmaskclient/eip/EipSetupObserver.java | 7 ++++--- .../leap/bitmaskclient/providersetup/ProviderAPI.java | 11 ----------- .../providersetup/ProviderApiManagerBase.java | 6 +----- .../activities/ProviderSetupBaseActivity.java | 19 +++++++++++++++---- 4 files changed, 20 insertions(+), 23 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java index f42a7d3d..1a2d8769 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -73,7 +73,6 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLO import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_NOK; -import static se.leap.bitmaskclient.providersetup.ProviderAPI.STOP_PROXY; import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; /** @@ -211,7 +210,8 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta case INCORRECTLY_DOWNLOADED_EIP_SERVICE: case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE: if (TorStatusObservable.getStatus() != OFF) { - ProviderAPICommand.execute(context.getApplicationContext(), STOP_PROXY, null); + Intent stopIntent = new Intent(context, TorService.class); + context.stopService(stopIntent); } default: break; @@ -360,7 +360,8 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta observedProfileFromVpnStatus = null; this.changingGateway.set(changingGateway); if (TorStatusObservable.getStatus() != OFF) { - ProviderAPICommand.execute(context.getApplicationContext(), STOP_PROXY, null); + Intent intent = new Intent(context, TorService.class); + context.stopService(intent); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index f2376568..f88f44a5 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -174,17 +174,6 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB return -1; } - @Override - public void stopTorConnection() { - if (TorStatusObservable.getStatus() != OFF) { - TorStatusObservable.updateState(this, STOPPING.toString()); - initTorServiceConnection(this); - if (torServiceConnection != null) { - torServiceConnection.torService.stopSelf(); - } - } - } - private ProviderApiManager initApiManager() { SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(getResources()); 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 8118c872..00cbcd3e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -149,8 +149,7 @@ public abstract class ProviderApiManagerBase { public interface ProviderApiServiceCallback { void broadcastEvent(Intent intent); - int initTorConnection(); - void stopTorConnection(); + int getTorHttpTunnelPort(); } private final ProviderApiServiceCallback serviceCallback; @@ -286,9 +285,6 @@ public abstract class ProviderApiManagerBase { ProviderObservable.getInstance().setProviderForDns(null); } break; - case STOP_PROXY: - serviceCallback.stopTorConnection(); - break; } } diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java index 40efd811..0297bc39 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java @@ -29,6 +29,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.json.JSONException; import org.json.JSONObject; +import org.torproject.jni.TorService; import se.leap.bitmaskclient.base.FragmentManagerEnhanced; import se.leap.bitmaskclient.base.models.Provider; @@ -38,6 +39,7 @@ import se.leap.bitmaskclient.providersetup.ProviderApiSetupBroadcastReceiver; import se.leap.bitmaskclient.providersetup.ProviderManager; import se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog; import se.leap.bitmaskclient.providersetup.ProviderSetupInterface; +import se.leap.bitmaskclient.tor.TorStatusObservable; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_PROVIDER_API_EVENT; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; @@ -51,6 +53,7 @@ import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.Provide import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.ProviderConfigState.SETTING_UP_PROVIDER; import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.ProviderConfigState.SHOWING_PROVIDER_DETAILS; import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.ProviderConfigState.SHOW_FAILED_DIALOG; +import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; /** * Created by cyberta on 19.08.18. @@ -90,7 +93,7 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity } else if (SHOW_FAILED_DIALOG == providerConfigState) { showProgressBar(); } else if (SHOWING_PROVIDER_DETAILS == providerConfigState) { - cancelSettingUpProvider(); + cancelSettingUpProvider(false); } else if (PENDING_SHOW_PROVIDER_DETAILS == providerConfigState) { showProviderDetails(); } @@ -156,9 +159,7 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity // -------- DownloadFailedDialogInterface ---v @Override public void cancelSettingUpProvider() { - providerConfigState = PROVIDER_NOT_SET; - provider = null; - hideProgressBar(); + cancelSettingUpProvider(true); } @Override @@ -167,6 +168,16 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity ProviderAPICommand.execute(this, UPDATE_PROVIDER_DETAILS, provider); } + public void cancelSettingUpProvider(boolean stopTor) { + if (stopTor && TorStatusObservable.getStatus() != OFF) { + Intent torServiceIntent = new Intent(getApplicationContext(), TorService.class); + stopService(torServiceIntent); + } + providerConfigState = PROVIDER_NOT_SET; + provider = null; + hideProgressBar(); + } + protected void restoreState(Bundle savedInstanceState) { super.restoreState(savedInstanceState); if (savedInstanceState == null) { -- cgit v1.2.3 From 9d16ae1456b6d6a2fc2eb67b23fc05bc878ba741 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 14 Jul 2021 23:55:21 +0200 Subject: minor language fixes in ProviderSetupBaseActivity --- .../providersetup/activities/ProviderSetupBaseActivity.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java index 0297bc39..e4569c51 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ProviderSetupBaseActivity.java @@ -34,8 +34,8 @@ import org.torproject.jni.TorService; import se.leap.bitmaskclient.base.FragmentManagerEnhanced; import se.leap.bitmaskclient.base.models.Provider; import se.leap.bitmaskclient.providersetup.ProviderAPICommand; -import se.leap.bitmaskclient.providersetup.ProviderDetailActivity; import se.leap.bitmaskclient.providersetup.ProviderApiSetupBroadcastReceiver; +import se.leap.bitmaskclient.providersetup.ProviderDetailActivity; import se.leap.bitmaskclient.providersetup.ProviderManager; import se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog; import se.leap.bitmaskclient.providersetup.ProviderSetupInterface; @@ -60,7 +60,7 @@ import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; */ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity implements ProviderSetupInterface, ProviderSetupFailedDialog.DownloadFailedDialogInterface { - final public static String TAG = "PoviderSetupActivity"; + final public static String TAG = "ProviderSetupActivity"; final private static String ACTIVITY_STATE = "ACTIVITY STATE"; final private static String REASON_TO_FAIL = "REASON TO FAIL"; @@ -207,7 +207,7 @@ public abstract class ProviderSetupBaseActivity extends ConfigWizardBaseActivity /** * Once selected a provider, this fragment offers the user to log in, * use it anonymously (if possible) - * or cancel his/her election pressing the back button. + * or cancel their selection pressing the back button. */ public void showProviderDetails() { // show only if current activity is shown -- cgit v1.2.3 From 99fd84e800758647b4a4e178f8f5f7112b54d66f Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 14 Jul 2021 23:59:34 +0200 Subject: rename method --- app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java | 2 +- .../se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index f88f44a5..15843697 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -154,7 +154,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB @Override - public int initTorConnection() { + public int getTorHttpTunnelPort() { initTorServiceConnection(this); if (torServiceConnection != null) { Intent torServiceIntent = new Intent(this, TorService.class); 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 00cbcd3e..f70be365 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -291,7 +291,7 @@ public abstract class ProviderApiManagerBase { protected int startTorProxy() { int port = -1; if (PreferenceHelper.useTor(preferences) && EipStatus.getInstance().isDisconnected() ) { - port = serviceCallback.initTorConnection(); + port = serviceCallback.getTorHttpTunnelPort(); if (port != -1) { try { waitForTorCircuits(); -- cgit v1.2.3 From 3c15fc1fb3cfb1537e2dfad740921c5f1d063259 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 15 Jul 2021 00:02:37 +0200 Subject: remove log in ProviderApiManagerBase --- .../java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java | 1 - 1 file changed, 1 deletion(-) (limited to 'app/src/main') 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 f70be365..0fcbcdae 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -166,7 +166,6 @@ public abstract class ProviderApiManagerBase { } public void handleIntent(Intent command) { -// Log.d(TAG, "handleIntent was called!"); ResultReceiver receiver = null; if (command.getParcelableExtra(RECEIVER_KEY) != null) { receiver = command.getParcelableExtra(RECEIVER_KEY); -- cgit v1.2.3 From aa43298684367000cadf16037cb90c366ac3c000 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 15 Jul 2021 00:04:36 +0200 Subject: ensure that response.body().close() is always called when reading non-null API responses --- .../bitmaskclient/providersetup/ProviderApiConnector.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java index c863abd4..35ad9cd2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java @@ -95,10 +95,15 @@ public class ProviderApiConnector { if (!response.isSuccessful()) { VpnStatus.logWarning("[API] API request failed: " + url); } - InputStream inputStream = response.body().byteStream(); - Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); - if (scanner.hasNext()) { - return scanner.next(); + + if (response.body() != null) { + InputStream inputStream = response.body().byteStream(); + Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"); + if (scanner.hasNext()) { + String result = scanner.next(); + response.body().close(); + return result; + } } return null; } -- cgit v1.2.3 From b2705ea702a9eaa4a0f5566ed70cdfb4f41390d3 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 15 Jul 2021 00:06:24 +0200 Subject: don't set a new ClientTransportPlugin everytime a service connection has been initiated with TorService --- .../main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index 15843697..15e77576 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -192,7 +192,9 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB try { if (torServiceConnection == null) { Log.d(TAG, "serviceConnection is still null"); - TorService.setClientTransportPlugin(new ClientTransportPlugin(context.getApplicationContext())); + if (!TorService.hasClientTransportPlugin()) { + TorService.setClientTransportPlugin(new ClientTransportPlugin(context.getApplicationContext())); + } torServiceConnection = new TorServiceConnection(context); } } catch (InterruptedException | IllegalStateException e) { -- cgit v1.2.3 From a3ed9483391ef38ee6665be4e4fe25a71b4c83be Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 15 Jul 2021 00:08:45 +0200 Subject: always stop tor service connection immediately after we got the http tunnel port --- .../main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index 15e77576..322bf1cd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -168,7 +168,10 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB getApplicationContext().startService(torServiceIntent); } - return torServiceConnection.torService.getHttpTunnelPort(); + int tunnelPort = torServiceConnection.torService.getHttpTunnelPort(); + torServiceConnection.close(); + torServiceConnection = null; + return tunnelPort; } return -1; -- cgit v1.2.3 From 818281a7fdf8b43437e14478a2276dc821b69284 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 15 Jul 2021 00:10:15 +0200 Subject: set torService to null when tor service connection disconnected --- app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java | 1 + 1 file changed, 1 insertion(+) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index 322bf1cd..42ded09f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -243,6 +243,7 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB @Override public void onServiceDisconnected(ComponentName name) { + torService = null; } }; Intent intent = new Intent(context, TorService.class); -- cgit v1.2.3 From 3d5675f2287f22c399d9a5f4bfcef999100a1b2c Mon Sep 17 00:00:00 2001 From: cyberta Date: Wed, 4 Aug 2021 10:36:56 +0200 Subject: update import path for shapeshifter lib --- .../java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java index d39f8bf3..c7893249 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java @@ -27,7 +27,7 @@ import java.util.Observer; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.eip.EipStatus; -import shapeshifter.ShapeShifter; +import shapeShifter.ShapeShifter; public class Shapeshifter implements Observer { @@ -43,7 +43,7 @@ public class Shapeshifter implements Observer { private int retry = 0; private Handler reconnectHandler; - public class ShapeshifterLogger implements shapeshifter.Logger { + public class ShapeshifterLogger implements shapeShifter.Logger { @Override public void log(String s) { Log.e(TAG, "SHAPESHIFTER ERROR: " + s); -- cgit v1.2.3 From 2da4c19827ed722f4dde2830bd84b21553a13bcd Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 30 Sep 2021 14:50:11 +0200 Subject: set tor setup timeout to 3 minutes --- .../se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src/main') 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 0fcbcdae..b7108225 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -314,7 +314,7 @@ public abstract class ProviderApiManagerBase { } }; TorStatusObservable.getInstance().addObserver(observer); - countDownLatch.await(90, TimeUnit.SECONDS); + countDownLatch.await(180, TimeUnit.SECONDS); } void resetProviderDetails(Provider provider) { -- cgit v1.2.3 From 780c63fc4afbe38eca70237e07165d88596b3843 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 30 Sep 2021 14:50:29 +0200 Subject: remove tor circuit setup observer once timeout reached or tor circuits have been setup successfully --- .../java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java | 1 + 1 file changed, 1 insertion(+) (limited to 'app/src/main') 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 b7108225..9ca4c746 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -315,6 +315,7 @@ public abstract class ProviderApiManagerBase { }; TorStatusObservable.getInstance().addObserver(observer); countDownLatch.await(180, TimeUnit.SECONDS); + TorStatusObservable.getInstance().deleteObserver(observer); } void resetProviderDetails(Provider provider) { -- cgit v1.2.3 From c8a6822c27210eba74299718679db101a6ee273e Mon Sep 17 00:00:00 2001 From: cyBerta Date: Thu, 30 Sep 2021 16:08:57 +0200 Subject: workaround for jni issue again --- .../se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java index c7893249..828b90ba 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java @@ -27,7 +27,7 @@ import java.util.Observer; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.eip.EipStatus; -import shapeShifter.ShapeShifter; +import shapeShifter.ShapeShifter_; public class Shapeshifter implements Observer { @@ -37,11 +37,11 @@ public class Shapeshifter implements Observer { private static final int RETRY_TIME = 4000; private static final String TAG = Shapeshifter.class.getSimpleName(); - private ShapeShifter shapeShifter; + private final ShapeShifter_ shapeShifter; private boolean isErrorHandling; private boolean noNetwork; private int retry = 0; - private Handler reconnectHandler; + private final Handler reconnectHandler; public class ShapeshifterLogger implements shapeShifter.Logger { @Override @@ -61,7 +61,7 @@ public class Shapeshifter implements Observer { } public Shapeshifter(Obfs4Options options) { - shapeShifter = new ShapeShifter(); + shapeShifter = new ShapeShifter_(); shapeShifter.setLogger(new ShapeshifterLogger()); setup(options); Looper.prepare(); -- cgit v1.2.3 From 80bf751141c85316c22a0d16c1e4d6fa0f473f44 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 2 Oct 2021 13:31:36 +0200 Subject: * refactor startTorProxy() * fix setting http proxy port correctly * snowflake+tor does currently only work when being connected to a wifi, not a cellular network. For now, we check if the device is connected to a wifi, before attempting to start tor --- .../java/de/blinkt/openvpn/core/NetworkUtils.java | 30 ++++++++++++++++ .../bitmaskclient/providersetup/ProviderAPI.java | 34 ++++++++++-------- .../providersetup/ProviderApiManagerBase.java | 41 ++++++++++++++-------- 3 files changed, 76 insertions(+), 29 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/de/blinkt/openvpn/core/NetworkUtils.java b/app/src/main/java/de/blinkt/openvpn/core/NetworkUtils.java index 78799a3a..cbb58f0f 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/NetworkUtils.java +++ b/app/src/main/java/de/blinkt/openvpn/core/NetworkUtils.java @@ -9,6 +9,9 @@ import android.content.Context; import android.net.*; import android.os.Build; import android.text.TextUtils; +import android.util.Log; + +import androidx.core.net.ConnectivityManagerCompat; import java.net.Inet4Address; import java.net.Inet6Address; @@ -16,6 +19,8 @@ import java.util.Vector; public class NetworkUtils { + private static final String TAG = NetworkUtils.class.getSimpleName(); + public static Vector getLocalNetworks(Context c, boolean ipv6) { Vector nets = new Vector<>(); ConnectivityManager conn = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -76,4 +81,29 @@ public class NetworkUtils { return nets; } + public static boolean isConnectedToWifi(Context context) { + ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) { + NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + Log.d(TAG, "isConnectedToWifi (<=LOLLIPOP_MR1): " + wifi.isConnected()); + return wifi.isConnected(); + } else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) { + NetworkInfo[] netInfos = connManager.getAllNetworkInfo(); + for (NetworkInfo netInfo : netInfos) { + if (netInfo != null && netInfo.getType() == ConnectivityManager.TYPE_WIFI) { + Log.d(TAG, "isConnectedToWifi (<= Build.VERSION_CODES.O_MR1): " + netInfo.isConnected()); + return netInfo.isConnected(); + } + } + } else { + NetworkInfo netInfo = connManager.getActiveNetworkInfo(); + if(netInfo != null) { + NetworkCapabilities networkCapabilities = connManager.getNetworkCapabilities(connManager.getActiveNetwork()); + Log.d(TAG, "isConnectedToWifi (> Build.VERSION_CODES.O_MR1): " + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)); + return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); + } + } + + return false; + } } \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java index 42ded09f..e818f587 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java @@ -37,17 +37,15 @@ import java.io.Closeable; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import de.blinkt.openvpn.core.NetworkUtils; import se.leap.bitmaskclient.base.utils.PreferenceHelper; import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator; import se.leap.bitmaskclient.tor.ClientTransportPlugin; import se.leap.bitmaskclient.tor.TorNotificationManager; -import se.leap.bitmaskclient.tor.TorStatusObservable; import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.base.utils.ConfigHelper.ensureNotOnMainThread; import static se.leap.bitmaskclient.tor.TorNotificationManager.TOR_SERVICE_NOTIFICATION_ID; -import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; -import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.STOPPING; /** * Implements HTTP api methods (encapsulated in {{@link ProviderApiManager}}) @@ -151,23 +149,31 @@ public class ProviderAPI extends JobIntentService implements ProviderApiManagerB LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } + @Override + public boolean isConnectedToWifi() { + return NetworkUtils.isConnectedToWifi(getApplicationContext()); + } + + @Override + public void startTorService() { + initTorServiceConnection(this); + Intent torServiceIntent = new Intent(this, TorService.class); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Notification notification = TorNotificationManager.buildTorForegroundNotification(getApplicationContext()); + //noinspection NewApi + getApplicationContext().startForegroundService(torServiceIntent); + torServiceConnection.torService.startForeground(TOR_SERVICE_NOTIFICATION_ID, notification); + } else { + getApplicationContext().startService(torServiceIntent); + } + } @Override public int getTorHttpTunnelPort() { initTorServiceConnection(this); if (torServiceConnection != null) { - Intent torServiceIntent = new Intent(this, TorService.class); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Notification notification = TorNotificationManager.buildTorForegroundNotification(getApplicationContext()); - //noinspection NewApi - getApplicationContext().startForegroundService(torServiceIntent); - torServiceConnection.torService.startForeground(TOR_SERVICE_NOTIFICATION_ID, notification); - } else { - getApplicationContext().startService(torServiceIntent); - } - int tunnelPort = torServiceConnection.torService.getHttpTunnelPort(); torServiceConnection.close(); torServiceConnection = null; diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java index 9ca4c746..780ecaed 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java @@ -51,6 +51,8 @@ import java.util.NoSuchElementException; import java.util.Observer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; @@ -149,7 +151,9 @@ public abstract class ProviderApiManagerBase { public interface ProviderApiServiceCallback { void broadcastEvent(Intent intent); + void startTorService(); int getTorHttpTunnelPort(); + boolean isConnectedToWifi(); } private final ProviderApiServiceCallback serviceCallback; @@ -188,7 +192,11 @@ public abstract class ProviderApiManagerBase { } // uncomment for testing --v - TorStatusObservable.setProxyPort(startTorProxy()); + /* try { + startTorProxy(); + } catch (InterruptedException | TimeoutException e) { + e.printStackTrace(); + } */ Bundle result = new Bundle(); switch (action) { @@ -287,35 +295,38 @@ public abstract class ProviderApiManagerBase { } } - protected int startTorProxy() { - int port = -1; - if (PreferenceHelper.useTor(preferences) && EipStatus.getInstance().isDisconnected() ) { - port = serviceCallback.getTorHttpTunnelPort(); - if (port != -1) { - try { - waitForTorCircuits(); - } catch (InterruptedException e) { - e.printStackTrace(); - port = -1; - } - } + protected boolean startTorProxy() throws InterruptedException, TimeoutException { + if (PreferenceHelper.useTor(preferences) && + EipStatus.getInstance().isDisconnected() && + serviceCallback.isConnectedToWifi() + ) { + serviceCallback.startTorService(); + waitForTorCircuits(); + int port = serviceCallback.getTorHttpTunnelPort(); + TorStatusObservable.setProxyPort(port); + return port != -1; } - return port; + return false; } - private void waitForTorCircuits() throws InterruptedException { + private void waitForTorCircuits() throws InterruptedException, TimeoutException { if (TorStatusObservable.getStatus() == ON) { return; } CountDownLatch countDownLatch = new CountDownLatch(1); + AtomicBoolean successfulSetup = new AtomicBoolean(false); Observer observer = (o, arg) -> { if (TorStatusObservable.getStatus() == ON) { + successfulSetup.set(true); countDownLatch.countDown(); } }; TorStatusObservable.getInstance().addObserver(observer); countDownLatch.await(180, TimeUnit.SECONDS); TorStatusObservable.getInstance().deleteObserver(observer); + if (!successfulSetup.get()) { + throw new TimeoutException("Timeout reached"); + } } void resetProviderDetails(Provider provider) { -- cgit v1.2.3 From cdd97d8bcc65ad80ed1060b297650b8350b27a56 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 2 Oct 2021 13:36:31 +0200 Subject: add log to see if a fetch was successful --- app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java index 1a2d8769..a71a5613 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -73,6 +73,7 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLO import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_NOK; +import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_OK; import static se.leap.bitmaskclient.tor.TorStatusObservable.TorStatus.OFF; /** @@ -213,6 +214,11 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta Intent stopIntent = new Intent(context, TorService.class); context.stopService(stopIntent); } + Log.d(TAG, "PROVIDER NOK - FETCH FAILED"); + break; + case PROVIDER_OK: + Log.d(TAG, "PROVIDER OK - FETCH SUCCESSFUL"); + break; default: break; } -- cgit v1.2.3 From 0f39fcfc827404a58153941d7ce1b57d272f56d7 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 2 Oct 2021 15:16:05 +0200 Subject: show snowflake logs in logcat --- .../bitmaskclient/tor/ClientTransportPlugin.java | 48 +++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) (limited to 'app/src/main') 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 9e3828de..d0385d1e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java @@ -1,6 +1,7 @@ package se.leap.bitmaskclient.tor; import android.content.Context; +import android.os.FileObserver; import android.util.Log; import androidx.annotation.Nullable; @@ -9,10 +10,14 @@ import org.torproject.jni.ClientTransportPluginInterface; import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.lang.ref.WeakReference; +import java.util.Collection; import java.util.HashMap; +import java.util.Scanner; +import java.util.Vector; import IPtProxy.IPtProxy; @@ -22,6 +27,7 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface { private HashMap mFronts; private final WeakReference contextRef; private long snowflakePort = -1; + private FileObserver logFileObserver; public ClientTransportPlugin(Context context) { this.contextRef = new WeakReference<>(context); @@ -36,12 +42,13 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface { } File logfile = new File(context.getApplicationContext().getCacheDir(), "snowflake.log"); Log.d(TAG, "logfile at " + logfile.getAbsolutePath()); - if (!logfile.exists()) { - try { - logfile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); + try { + if (logfile.exists()) { + logfile.delete(); } + logfile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); } //this is using the current, default Tor snowflake infrastructure @@ -51,12 +58,43 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface { Log.d(TAG, "startSnowflake. target: " + target + ", front:" + front + ", stunServer" + stunServer); snowflakePort = IPtProxy.startSnowflake( stunServer, target, front, logfile.getAbsolutePath(), false, false, true, 1); Log.d(TAG, "startSnowflake running on port: " + snowflakePort); + watchLogFile(logfile); + } + + private void watchLogFile(File logfile) { + final Vector lastBuffer = new Vector<>(); + logFileObserver = new FileObserver(logfile) { + @Override + public void onEvent(int event, @Nullable String name) { + if (FileObserver.MODIFY == event) { + try (Scanner scanner = new Scanner(logfile)) { + Vector currentBuffer = new Vector<>(); + while (scanner.hasNextLine()) { + currentBuffer.add(scanner.nextLine()); + } + if (lastBuffer.size() < currentBuffer.size()) { + int startIndex = lastBuffer.size() > 0 ? lastBuffer.size() - 1 : 0; + int endIndex = currentBuffer.size() - 1; + Collection newMessages = currentBuffer.subList(startIndex, endIndex); + for (String message : newMessages) { + Log.d("[SNOWFLAKE]", message); + } + lastBuffer.addAll(newMessages); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + } + }; + logFileObserver.startWatching(); } @Override public void stop() { IPtProxy.stopSnowflake(); snowflakePort = -1; + logFileObserver.stopWatching(); } @Override -- cgit v1.2.3 From 1d6bdaf993b2235f7ebc7ca9d2f3ebc5dee8c9cc Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 2 Oct 2021 15:18:32 +0200 Subject: set max snowflake peers to 5 for now --- app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src/main') 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 d0385d1e..0fd51d15 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java @@ -56,7 +56,7 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface { String front = getCdnFront("snowflake-front"); String stunServer = getCdnFront("snowflake-stun"); Log.d(TAG, "startSnowflake. target: " + target + ", front:" + front + ", stunServer" + stunServer); - snowflakePort = IPtProxy.startSnowflake( stunServer, target, front, logfile.getAbsolutePath(), false, false, true, 1); + snowflakePort = IPtProxy.startSnowflake( stunServer, target, front, logfile.getAbsolutePath(), false, false, true, 5); Log.d(TAG, "startSnowflake running on port: " + snowflakePort); watchLogFile(logfile); } -- cgit v1.2.3 From dac7d9ce22f1eb2a96cc8a3a0fc34e6c3d4c9291 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 16 Oct 2021 11:56:25 +0200 Subject: update shapeshifter jni bindings --- .../se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java index 828b90ba..970703cc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Shapeshifter.java @@ -27,7 +27,7 @@ import java.util.Observer; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.eip.EipStatus; -import shapeShifter.ShapeShifter_; +import shapeshifter.ShapeShifter; public class Shapeshifter implements Observer { @@ -37,13 +37,13 @@ public class Shapeshifter implements Observer { private static final int RETRY_TIME = 4000; private static final String TAG = Shapeshifter.class.getSimpleName(); - private final ShapeShifter_ shapeShifter; + private final shapeshifter.ShapeShifter shapeShifter; private boolean isErrorHandling; private boolean noNetwork; private int retry = 0; private final Handler reconnectHandler; - public class ShapeshifterLogger implements shapeShifter.Logger { + public class ShapeshifterLogger implements shapeshifter.Logger { @Override public void log(String s) { Log.e(TAG, "SHAPESHIFTER ERROR: " + s); @@ -61,7 +61,7 @@ public class Shapeshifter implements Observer { } public Shapeshifter(Obfs4Options options) { - shapeShifter = new ShapeShifter_(); + shapeShifter = new ShapeShifter(); shapeShifter.setLogger(new ShapeshifterLogger()); setup(options); Looper.prepare(); -- cgit v1.2.3 From fcec92a0042477347338a25cd072d622edfa03c9 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 16 Oct 2021 11:59:42 +0200 Subject: show tor bootstrapping progress in notifications --- .../leap/bitmaskclient/eip/EipSetupObserver.java | 4 +- .../bitmaskclient/tor/TorNotificationManager.java | 13 +++- .../bitmaskclient/tor/TorStatusObservable.java | 77 +++++++++++++++++++++- app/src/main/res/values/strings.xml | 18 ++++- 4 files changed, 104 insertions(+), 8 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java index a71a5613..23fbdc0c 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -169,7 +169,9 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta private void handleTorStatusEvent(Intent intent) { String status = intent.getStringExtra(TorService.EXTRA_STATUS); Log.d(TAG, "handle Tor status event: " + status); - TorStatusObservable.updateState(context, status); + Integer bootstrap = intent.getIntExtra(TorService.EXTRA_STATUS_DETAIL_BOOTSTRAP, -1); + String logKey = intent.getStringExtra(TorService.EXTRA_STATUS_DETAIL_LOGKEY); + TorStatusObservable.updateState(context, status, bootstrap, logKey); } diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java index a2401732..8148fd94 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java @@ -50,7 +50,7 @@ public class TorNotificationManager { .setContentText(context.getString(R.string.tor_started)).build(); } - public void buildTorNotification(Context context, String state) { + public void buildTorNotification(Context context, String state, String message, int progress) { NotificationManager notificationManager = initNotificationManager(context); if (notificationManager == null) { return; @@ -59,8 +59,15 @@ public class TorNotificationManager { notificationBuilder .setSmallIcon(R.drawable.ic_bridge_36) .setWhen(System.currentTimeMillis()) - .setTicker(state) - .setContentText(state); + .setStyle(new NotificationCompat.BigTextStyle(). + setBigContentTitle(state). + bigText(message)) + .setTicker(message) + .setContentTitle(state) + .setContentText(message); + if (progress > 0) { + notificationBuilder.setProgress(100, progress, false); + } notificationManager.notify(TOR_SERVICE_NOTIFICATION_ID, notificationBuilder.build()); } 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 e806e441..11baecbf 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.Observable; +import java.util.Vector; import se.leap.bitmaskclient.R; @@ -28,6 +29,8 @@ public class TorStatusObservable extends Observable { private final TorNotificationManager torNotificationManager; private String lastError; private int port = -1; + private int bootstrapPercent = -1; + private Vector lastLogs = new Vector<>(100); private TorStatusObservable() { torNotificationManager = new TorNotificationManager(); @@ -44,25 +47,93 @@ public class TorStatusObservable extends Observable { return getInstance().status; } + public static void logMessage(Context context, String tag, String message) { + Log.d(tag, message); + addLog(message); + if (getInstance().status != TorStatus.OFF) { + getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), message, getInstance().bootstrapPercent); + } + instance.setChanged(); + instance.notifyObservers(); + } + + private static void addLog(String message) { + if (instance.lastLogs.size() > 100) { + instance.lastLogs.remove(0); + } + instance.lastLogs.add(message); + } public static void updateState(Context context, String status) { + updateState(context,status, -1, null); + } + + public static void updateState(Context context, String status, int bootstrapPercent, @Nullable String logKey) { try { - Log.d(TAG, "update tor state: " + status); + Log.d(TAG, "update tor state: " + status + " " + bootstrapPercent + " "+ logKey); getInstance().status = TorStatus.valueOf(status); + if (bootstrapPercent != -1) { + getInstance().bootstrapPercent = bootstrapPercent; + } + int progress = getInstance().status == TorStatus.STARTING ? getInstance().bootstrapPercent : -1; + + if (getInstance().status == TorStatus.OFF) { getInstance().torNotificationManager.cancelNotifications(context); + } else if (logKey != null) { + String log = getStringFor(context, logKey); + addLog(log); + getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), log, progress); } else { - getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context)); + String log = instance.lastLogs.size() > 0 ? instance.lastLogs.lastElement() : ""; + getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), log, progress); } + instance.setChanged(); instance.notifyObservers(); - } catch (IllegalStateException e) { e.printStackTrace(); } } + private static String getStringFor(Context context, String key) { + switch (key) { + case "conn_pt": + return context.getString(R.string.log_conn_pt); + case "conn_done_pt": + return context.getString(R.string.log_conn_done_pt); + case "conn_done": + return context.getString(R.string.log_conn_done); + case "handshake": + return context.getString(R.string.log_handshake); + case "handshake_done": + return context.getString(R.string.log_handshake_done); + case "onehop_create": + return context.getString(R.string.log_onehop_create); + case "requesting_status": + return context.getString(R.string.log_requesting_status); + case "loading_status": + return context.getString(R.string.log_loading_status); + case "loading_keys": + return context.getString(R.string.log_loading_keys); + case "requesting_descriptors": + return context.getString(R.string.log_requesting_desccriptors); + case "loading_descriptors": + return context.getString(R.string.log_loading_descriptors); + case "enough_dirinfo": + return context.getString(R.string.log_enough_dirinfo); + case "ap_handshake_done": + return context.getString(R.string.log_ap_handshake_done); + case "circuit_create": + return context.getString(R.string.log_circuit_create); + case "done": + return context.getString(R.string.log_done); + default: + return key; + } + } + public static void setLastError(String error) { getInstance().lastError = error; instance.setChanged(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7c0e6334..dfb0db0e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,8 +155,24 @@ Automatic Your traffic is currently routed through: - Starting bridges for censorship circumvention. Please hold on… + Starting bridges for censorship circumvention… Stopping bridges. Using bridges for censorship circumvention. + Connected to pluggable transport + Connecting to pluggable transport + Connected to a relay + Handshaking with a relay + Handshake with a relay done + Establishing an encrypted directory connection + Asking for networkstatus consensus + Loading networkstatus consensus + Loading authority key certs + Asking for relay descriptors + Loading relay descriptors + Loaded enough directory info to build circuits + Handshake finished with a relay to build circuits + Establishing a Tor circuit + Done! + -- cgit v1.2.3 From eeafd2b56a7996b163072e57cec549b9769ccd0d Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 16 Oct 2021 12:00:23 +0200 Subject: don't recompile pattern in while loop --- app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java index fc77d9a5..9d307b02 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java @@ -140,6 +140,8 @@ public class OpenVPNThread implements Runnable { InputStream in = mProcess.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); + // 1380308330.240114 18000002 Send to HTTP proxy: 'X-Online-Host: bla.blabla.com' + Pattern p = Pattern.compile("(\\d+).(\\d+) ([0-9a-f])+ (.*)"); while (true) { String logline = br.readLine(); if (logline == null) @@ -151,10 +153,6 @@ public class OpenVPNThread implements Runnable { if (logline.startsWith(BROKEN_PIE_SUPPORT) || logline.contains(BROKEN_PIE_SUPPORT2)) mBrokenPie = true; - - // 1380308330.240114 18000002 Send to HTTP proxy: 'X-Online-Host: bla.blabla.com' - - Pattern p = Pattern.compile("(\\d+).(\\d+) ([0-9a-f])+ (.*)"); Matcher m = p.matcher(logline); int logerror = 0; if (m.matches()) { -- cgit v1.2.3 From bb2f6caa4ed2fae8978c578be829dcc6b08d542b Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 16 Oct 2021 13:52:03 +0200 Subject: show snowflake logs in notification --- .../bitmaskclient/tor/ClientTransportPlugin.java | 24 ++++++++++++- .../bitmaskclient/tor/TorStatusObservable.java | 42 +++++++++++++++------- 2 files changed, 52 insertions(+), 14 deletions(-) (limited to 'app/src/main') 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 0fd51d15..1d2b7cea 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java @@ -18,6 +18,8 @@ import java.util.Collection; import java.util.HashMap; import java.util.Scanner; import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import IPtProxy.IPtProxy; @@ -28,6 +30,7 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface { private final WeakReference contextRef; private long snowflakePort = -1; private FileObserver logFileObserver; + private static final Pattern SNOWFLAKE_LOG_TIMESTAMP_PATTERN = Pattern.compile("((19|2[0-9])[0-9]{2}\\/\\d{1,2}\\/\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}) ([\\S|\\s]+)"); public ClientTransportPlugin(Context context) { this.contextRef = new WeakReference<>(context); @@ -77,7 +80,7 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface { int endIndex = currentBuffer.size() - 1; Collection newMessages = currentBuffer.subList(startIndex, endIndex); for (String message : newMessages) { - Log.d("[SNOWFLAKE]", message); + logSnowflakeMessage(message); } lastBuffer.addAll(newMessages); } @@ -130,4 +133,23 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface { } return null; } + + private void logSnowflakeMessage(String message) { + Matcher matcher = SNOWFLAKE_LOG_TIMESTAMP_PATTERN.matcher(message); + if (matcher.matches()) { + try { + + String strippedString = matcher.group(3).trim(); + Log.d(TAG, "log: " + message); + Log.d(TAG, "log stripped: " + strippedString); + if (strippedString.length() > 0) { + TorStatusObservable.logSnowflakeMessage(contextRef.get(), strippedString); + } + } catch (IndexOutOfBoundsException | IllegalStateException e) { + e.printStackTrace(); + } + } else { + TorStatusObservable.logSnowflakeMessage(contextRef.get(), message); + } + } } 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 11baecbf..64e68727 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java @@ -1,10 +1,8 @@ package se.leap.bitmaskclient.tor; import android.content.Context; -import android.os.Handler; import android.util.Log; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.Observable; @@ -24,10 +22,15 @@ public class TorStatusObservable extends Observable { UNKOWN } + public static final String LOG_TAG_TOR = "[TOR]"; + public static final String LOG_TAG_SNOWFLAKE = "[SNOWFLAKE]"; + private static TorStatusObservable instance; private TorStatus status = TorStatus.UNKOWN; private final TorNotificationManager torNotificationManager; private String lastError; + private String lastTorLog; + private String lastSnowflakeLog; private int port = -1; private int bootstrapPercent = -1; private Vector lastLogs = new Vector<>(100); @@ -47,16 +50,32 @@ public class TorStatusObservable extends Observable { return getInstance().status; } - public static void logMessage(Context context, String tag, String message) { - Log.d(tag, message); + public static void logSnowflakeMessage(Context context, String message) { + Log.d(LOG_TAG_SNOWFLAKE, message); addLog(message); + getInstance().lastSnowflakeLog = message; if (getInstance().status != TorStatus.OFF) { - getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), message, getInstance().bootstrapPercent); + getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getNotificationProgress()); } instance.setChanged(); instance.notifyObservers(); } + private static String getNotificationLog() { + String snowflakeIcon = new String(Character.toChars(0x2744)); + String snowflakeLog = getInstance().lastSnowflakeLog; + // we don't want to show the response json in the notification + if (snowflakeLog != null && snowflakeLog.contains("Received answer: {")) { + snowflakeLog = "Received answer."; + } + return "Tor: " + getInstance().lastTorLog + "\n" + + snowflakeIcon + ": " + snowflakeLog; + } + + private static int getNotificationProgress() { + return getInstance().status == TorStatus.STARTING ? getInstance().bootstrapPercent : -1; + } + private static void addLog(String message) { if (instance.lastLogs.size() > 100) { instance.lastLogs.remove(0); @@ -75,18 +94,15 @@ public class TorStatusObservable extends Observable { if (bootstrapPercent != -1) { getInstance().bootstrapPercent = bootstrapPercent; } - int progress = getInstance().status == TorStatus.STARTING ? getInstance().bootstrapPercent : -1; - if (getInstance().status == TorStatus.OFF) { getInstance().torNotificationManager.cancelNotifications(context); - } else if (logKey != null) { - String log = getStringFor(context, logKey); - addLog(log); - getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), log, progress); } else { - String log = instance.lastLogs.size() > 0 ? instance.lastLogs.lastElement() : ""; - getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), log, progress); + if (logKey != null) { + getInstance().lastTorLog = getStringFor(context, logKey); + addLog(getInstance().lastTorLog); + } + getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getNotificationProgress()); } instance.setChanged(); -- cgit v1.2.3 From f6b8a78812c46679cf416edc303d88e840750827 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 16 Oct 2021 13:52:24 +0200 Subject: cleanup imports --- .../main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java | 3 --- 1 file changed, 3 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java index 8148fd94..45570857 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorNotificationManager.java @@ -23,12 +23,9 @@ import android.app.NotificationManager; import android.content.Context; import android.os.Build; -import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.base.models.Provider; -import se.leap.bitmaskclient.base.models.ProviderObservable; public class TorNotificationManager { public final static int TOR_SERVICE_NOTIFICATION_ID = 10; -- cgit v1.2.3 From 527c814c56626ed147778ac1e0ddabe70cb51280 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 16 Oct 2021 14:12:05 +0200 Subject: tweak tor done progess string --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src/main') diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dfb0db0e..4c5cffd4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -172,7 +172,7 @@ Loaded enough directory info to build circuits Handshake finished with a relay to build circuits Establishing a Tor circuit - Done! + Running -- cgit v1.2.3 From b4854af31a5026dd5129d994dd9d505dc4a63d70 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 16 Oct 2021 14:15:11 +0200 Subject: remove unneeded logs --- app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java | 3 --- 1 file changed, 3 deletions(-) (limited to 'app/src/main') 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 1d2b7cea..6228453e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/ClientTransportPlugin.java @@ -138,10 +138,7 @@ public class ClientTransportPlugin implements ClientTransportPluginInterface { Matcher matcher = SNOWFLAKE_LOG_TIMESTAMP_PATTERN.matcher(message); if (matcher.matches()) { try { - String strippedString = matcher.group(3).trim(); - Log.d(TAG, "log: " + message); - Log.d(TAG, "log stripped: " + strippedString); if (strippedString.length() > 0) { TorStatusObservable.logSnowflakeMessage(contextRef.get(), strippedString); } -- cgit v1.2.3 From 726f06f564e2743048baea5624daa96a511aace3 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Sat, 16 Oct 2021 15:31:33 +0200 Subject: tweak snowflake log message --- app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/src/main') 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 64e68727..449955af 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java @@ -66,7 +66,7 @@ public class TorStatusObservable extends Observable { String snowflakeLog = getInstance().lastSnowflakeLog; // we don't want to show the response json in the notification if (snowflakeLog != null && snowflakeLog.contains("Received answer: {")) { - snowflakeLog = "Received answer."; + snowflakeLog = "Received Answer."; } return "Tor: " + getInstance().lastTorLog + "\n" + snowflakeIcon + ": " + snowflakeLog; -- cgit v1.2.3 From 213e42d82d360cdf7f5632782a9a0cb879c1b4f5 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 22 Oct 2021 23:36:34 +0200 Subject: show tor and snowflake connection status in provider setup screens --- .../activities/ConfigWizardBaseActivity.java | 161 ++++++++++++++++++++- .../bitmaskclient/tor/TorStatusObservable.java | 23 ++- app/src/main/res/drawable/ic_snowflake.png | Bin 0 -> 2653 bytes app/src/main/res/drawable/ic_tor.png | Bin 0 -> 10221 bytes app/src/main/res/drawable/v_vertical_gradient.xml | 8 + .../main/res/layout-xlarge/v_loading_screen.xml | 138 ++++++++++++++++++ app/src/main/res/layout/v_add_provider.xml | 57 -------- app/src/main/res/layout/v_loading_screen.xml | 139 +++++++++++++++++- app/src/main/res/layout/v_log_item.xml | 16 ++ app/src/main/res/values/strings.xml | 1 + 10 files changed, 474 insertions(+), 69 deletions(-) create mode 100644 app/src/main/res/drawable/ic_snowflake.png create mode 100644 app/src/main/res/drawable/ic_tor.png create mode 100644 app/src/main/res/drawable/v_vertical_gradient.xml delete mode 100644 app/src/main/res/layout/v_add_provider.xml create mode 100644 app/src/main/res/layout/v_log_item.xml (limited to 'app/src/main') diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java index 7d452200..a0c046de 100644 --- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/ConfigWizardBaseActivity.java @@ -5,20 +5,28 @@ import android.graphics.PorterDuff; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.widget.Button; import android.widget.LinearLayout; import android.widget.ProgressBar; +import android.widget.RelativeLayout; import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.widget.AppCompatTextView; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.Guideline; import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import java.util.List; import java.util.Observable; import java.util.Observer; @@ -31,8 +39,14 @@ import se.leap.bitmaskclient.tor.TorStatusObservable; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getBootstrapProgress; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastLogs; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastSnowflakeLog; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getLastTorLog; +import static se.leap.bitmaskclient.tor.TorStatusObservable.getStringForCurrentStatus; /** * Base Activity for configuration wizard activities @@ -54,6 +68,32 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple @BindView(R.id.loading_screen) protected LinearLayout loadingScreen; + @Nullable + @BindView(R.id.btn_connection_detail) + protected Button connectionDetailBtn; + + @Nullable + @BindView(R.id.connection_detail_container) + protected RelativeLayout connectionDetailContainer; + + @Nullable + @BindView(R.id.log_container) + protected RelativeLayout logsContainer; + + @Nullable + @BindView(R.id.tor_state) + protected AppCompatTextView torState; + + @Nullable + @BindView(R.id.snowflake_state) + protected AppCompatTextView snowflakeState; + + @Nullable + @BindView(R.id.connection_detail_logs) + protected RecyclerView connectionDetailLogs; + + private TorLogAdapter torLogAdapter; + @Nullable @BindView(R.id.progressbar) protected ProgressBar progressBar; @@ -157,7 +197,7 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple super.onResume(); isActivityShowing = true; TorStatusObservable.getInstance().addObserver(this); - setProgressbarDescription(TorStatusObservable.getStringForCurrentStatus(this)); + setProgressbarDescription(getStringForCurrentStatus(this)); } protected void restoreState(Bundle savedInstanceState) { @@ -178,10 +218,48 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple providerHeaderView.setTitle(providerHeaderText); } + protected void showConnectionDetails() { + if (loadingScreen == null) { + return; + } + LinearLayoutManager layoutManager = new LinearLayoutManager(this); + connectionDetailLogs.setLayoutManager(layoutManager); + connectionDetailLogs.addItemDecoration( new DividerItemDecoration(this, layoutManager.getOrientation())); + torLogAdapter = new TorLogAdapter(getLastLogs()); + connectionDetailLogs.setAdapter(torLogAdapter); + + connectionDetailLogs.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (newState != SCROLL_STATE_IDLE) { + torLogAdapter.postponeUpdate = true; + } else if (newState == SCROLL_STATE_IDLE && getFirstVisibleItemPosion() == 0) { + torLogAdapter.postponeUpdate = false; + } + } + }); + + snowflakeState.setText(getLastSnowflakeLog()); + torState.setText(getLastTorLog()); + connectionDetailBtn.setOnClickListener(v -> { + connectionDetailBtn.setVisibility(GONE); + logsContainer.setVisibility(VISIBLE); + }); + connectionDetailContainer.setVisibility(VISIBLE); + } + + private int getFirstVisibleItemPosion() { + return ((LinearLayoutManager)connectionDetailLogs.getLayoutManager()).findFirstVisibleItemPosition(); + } + protected void hideProgressBar() { if (loadingScreen == null) { return; } + connectionDetailBtn.setVisibility(VISIBLE); + connectionDetailContainer.setVisibility(GONE); + logsContainer.setVisibility(GONE); loadingScreen.setVisibility(GONE); content.setVisibility(VISIBLE); } @@ -195,19 +273,28 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple } protected void setProgressbarTitle(@StringRes int progressbarTitle) { - if (this.progressbarTitle == null) { + if (loadingScreen == null) { return; } this.progressbarTitle.setText(progressbarTitle); } protected void setProgressbarDescription(String progressbarDescription) { - if (this.progressbarDescription == null) { + if (loadingScreen == null) { return; } this.progressbarDescription.setText(progressbarDescription); } + protected void setConfigProgress(int value) { + if (loadingScreen == null) { + return; + } + + progressBar.setProgress(value, true); + progressBar.setIndeterminate(value >= 100 || value < 0); + } + protected void showCompactLayout() { if (isCompactLayout) { @@ -307,7 +394,73 @@ public abstract class ConfigWizardBaseActivity extends ButterKnifeActivity imple @Override public void update(Observable o, Object arg) { if (o instanceof TorStatusObservable) { - runOnUiThread(() -> setProgressbarDescription(TorStatusObservable.getStringForCurrentStatus(ConfigWizardBaseActivity.this))); + runOnUiThread(() -> { + if (TorStatusObservable.getStatus() != TorStatusObservable.TorStatus.OFF && loadingScreen != null) { + if (connectionDetailContainer.getVisibility() == GONE) { + showConnectionDetails(); + } else { + setLogs(getLastTorLog(), getLastSnowflakeLog(), getLastLogs()); + } + } + setProgressbarDescription(getStringForCurrentStatus(ConfigWizardBaseActivity.this)); + setConfigProgress(getBootstrapProgress()); + }); + } + } + + protected void setLogs(String torLog, String snowflakeLog, List lastLogs) { + if (loadingScreen == null) { + return; + } + torLogAdapter.updateData(lastLogs); + torState.setText(torLog); + snowflakeState.setText(snowflakeLog); + } + + static class TorLogAdapter extends RecyclerView.Adapter { + private List values; + private boolean postponeUpdate; + + static class ViewHolder extends RecyclerView.ViewHolder { + public AppCompatTextView logTextLabel; + public View layout; + + public ViewHolder(View v) { + super(v); + layout = v; + logTextLabel = v.findViewById(android.R.id.text1); + } + } + + public void updateData(List data) { + values = data; + if (!postponeUpdate) { + notifyDataSetChanged(); + } + } + + public TorLogAdapter(List data) { + values = data; + } + + @NonNull + @Override + public TorLogAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from( + parent.getContext()); + View v = inflater.inflate(R.layout.v_log_item, parent, false); + return new TorLogAdapter.ViewHolder(v); + } + + @Override + public void onBindViewHolder(TorLogAdapter.ViewHolder holder, final int position) { + final String log = values.get(position); + holder.logTextLabel.setText(log); + } + + @Override + public int getItemCount() { + return values.size(); } } } diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java index 449955af..281b21c0 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java @@ -55,7 +55,7 @@ public class TorStatusObservable extends Observable { addLog(message); getInstance().lastSnowflakeLog = message; if (getInstance().status != TorStatus.OFF) { - getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getNotificationProgress()); + getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getBootstrapProgress()); } instance.setChanged(); instance.notifyObservers(); @@ -72,15 +72,15 @@ public class TorStatusObservable extends Observable { snowflakeIcon + ": " + snowflakeLog; } - private static int getNotificationProgress() { + public static int getBootstrapProgress() { return getInstance().status == TorStatus.STARTING ? getInstance().bootstrapPercent : -1; } private static void addLog(String message) { if (instance.lastLogs.size() > 100) { - instance.lastLogs.remove(0); + instance.lastLogs.remove(99); } - instance.lastLogs.add(message); + instance.lastLogs.add(0, message.trim()); } public static void updateState(Context context, String status) { @@ -102,7 +102,7 @@ public class TorStatusObservable extends Observable { getInstance().lastTorLog = getStringFor(context, logKey); addLog(getInstance().lastTorLog); } - getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getNotificationProgress()); + getInstance().torNotificationManager.buildTorNotification(context, getStringForCurrentStatus(context), getNotificationLog(), getBootstrapProgress()); } instance.setChanged(); @@ -168,8 +168,17 @@ public class TorStatusObservable extends Observable { @Nullable - public String getLastError() { - return lastError; + public static String getLastTorLog() { + return getInstance().lastTorLog; + } + + @Nullable + public static String getLastSnowflakeLog() { + return getInstance().lastSnowflakeLog; + } + + public static Vector getLastLogs() { + return getInstance().lastLogs; } public static String getStringForCurrentStatus(Context context) { diff --git a/app/src/main/res/drawable/ic_snowflake.png b/app/src/main/res/drawable/ic_snowflake.png new file mode 100644 index 00000000..992662ee Binary files /dev/null and b/app/src/main/res/drawable/ic_snowflake.png differ diff --git a/app/src/main/res/drawable/ic_tor.png b/app/src/main/res/drawable/ic_tor.png new file mode 100644 index 00000000..a5f9ae89 Binary files /dev/null and b/app/src/main/res/drawable/ic_tor.png differ diff --git a/app/src/main/res/drawable/v_vertical_gradient.xml b/app/src/main/res/drawable/v_vertical_gradient.xml new file mode 100644 index 00000000..877634b5 --- /dev/null +++ b/app/src/main/res/drawable/v_vertical_gradient.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/layout-xlarge/v_loading_screen.xml b/app/src/main/res/layout-xlarge/v_loading_screen.xml index ed25f07b..0b71099e 100644 --- a/app/src/main/res/layout-xlarge/v_loading_screen.xml +++ b/app/src/main/res/layout-xlarge/v_loading_screen.xml @@ -56,4 +56,142 @@ android:layout_marginTop="@dimen/standard_margin" /> + + + + + + + + +