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 --- .../bitmaskclient/providersetup/ProviderAPI.java | 134 +++++++++++++++++++++ 1 file changed, 134 insertions(+) (limited to 'app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderAPI.java') 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; + } + } + } -- cgit v1.2.3