From b9b379d4b04ecc018c645785d497aaac3b0c84d0 Mon Sep 17 00:00:00 2001 From: Matthew Finkel Date: Wed, 14 Nov 2018 17:36:53 +0000 Subject: [PATCH] Bug 28051 - Integrate Orbot and add dependencies Also: Bug 28051 - Launch Orbot if it isn't running in the background Bug 28329 - Part 2. Implement checking if the Tor service is running Bug 28329 - Part 3. Remove OrbotActivity dependency Bug 28051 - Stop the background service when we're quitting If the user swips away the app, then initiate quitting as if the user selected Quit from the menu. --- build.gradle | 4 + mobile/android/app/build.gradle | 13 ++ mobile/android/base/AndroidManifest.xml.in | 8 + .../java/org/mozilla/gecko/BrowserApp.java | 142 ++++++++++++++++++ .../base/java/org/mozilla/gecko/GeckoApp.java | 10 ++ .../org/mozilla/gecko/GeckoApplication.java | 5 + mobile/android/config/proguard/proguard.cfg | 14 ++ 7 files changed, 196 insertions(+) diff --git a/build.gradle b/build.gradle index 8b91888b5d7f5..95dfc2ed13232 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,10 @@ allprojects { url repository } } + // These are needed for Orbot's dependencies + maven { url "https://raw.githubusercontent.com/guardianproject/gpmaven/master" } + maven { url 'https://jitpack.io' } + jcenter() } task downloadDependencies() { diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index c6a0bc45d56fa..be0ccdb1b13f0 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -235,6 +235,14 @@ dependencies { // to generate the `Application` class or fork the file on disk. implementation "com.android.support:multidex:1.0.3" + // tor-android-services + implementation files('service-release.aar') + implementation files('jsocksAndroid-release.aar') + + // Tor_Onion_Proxy_Library + implementation files('universal-0.0.3.jar') + implementation files('android-release.aar') + if (mozconfig.substs.MOZ_NATIVE_DEVICES) { implementation "com.android.support:mediarouter-v7:$support_library_version" implementation "com.google.android.gms:play-services-basement:$google_play_services_version" @@ -277,6 +285,11 @@ dependencies { // Including the Robotium JAR directly can cause issues with dexing. androidTestImplementation 'com.jayway.android.robotium:robotium-solo:5.5.4' + + // tor-android-service Dependencies + implementation 'net.freehaven.tor.control:jtorctl:0.2' + implementation 'org.slf4j:slf4j-api:1.7.25' + implementation 'org.slf4j:slf4j-android:1.7.25' } // TODO: (bug 1261486): This impl is not robust - diff --git a/mobile/android/base/AndroidManifest.xml.in b/mobile/android/base/AndroidManifest.xml.in index 00e26babd2aca..44cd4800681cb 100644 --- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -543,5 +543,13 @@ #endif + + + + diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java index 2d46059737964..6fa6ce2272ca7 100644 --- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -10,11 +10,13 @@ import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.app.DownloadManager; +import android.content.BroadcastReceiver; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; @@ -41,6 +43,8 @@ import android.support.annotation.StringRes; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; +import android.support.v4.app.NotificationCompat; +import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.res.ResourcesCompat; import android.support.v4.view.MenuItemCompat; import android.text.TextUtils; @@ -177,6 +181,9 @@ import org.mozilla.geckoview.DynamicToolbarAnimator; import org.mozilla.geckoview.DynamicToolbarAnimator.PinReason; import org.mozilla.geckoview.GeckoSession; +import org.torproject.android.service.TorService; +import org.torproject.android.service.TorServiceConstants; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -252,6 +259,8 @@ public class BrowserApp extends GeckoApp private HomeScreen mHomeScreen; private TabsPanel mTabsPanel; + private boolean mTorNeedsStart = true; + private boolean showSplashScreen = false; private SplashScreen splashScreen; /** @@ -986,6 +995,130 @@ public class BrowserApp extends GeckoApp TelemetryUploadService.setDisabled(isInAutomation); } + /** + * Send the service a request for the current status. + * The response is sent as a broadcast. Capture that in + * receiveTorIsStartedAsync(). + */ + private void requestTorIsStartedAsync() { + Intent torServiceStatus = new Intent(this, TorService.class); + torServiceStatus.setAction(TorServiceConstants.ACTION_STATUS); + startService(torServiceStatus); + } + + private BroadcastReceiver mLocalBroadcastReceiver; + private Boolean mTorStatus; + + /** + * Setup the status receiver for broadcasts from the service. + * The response is sent as a broadcast. Create a background thread + * for receiving/handling the broadcast. + * + * This method is coupled with receiveTorIsStartedAsync(). They should + * be used together. + */ + private BroadcastReceiver setupReceiveTorIsStartedAsync() { + + // Create a thread specifically for defining the BroadcastReceiver + new Thread(new Runnable() { + + @Override + public void run() { + mLocalBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action == null) { + return; + } + + // We only want ACTION_STATUS messages + if (!action.equals(TorServiceConstants.ACTION_STATUS)) { + return; + } + + // The current status has the EXTRA_STATUS key + String currentStatus = + intent.getStringExtra(TorServiceConstants.EXTRA_STATUS); + + try { + synchronized (mTorStatus) { + mTorStatus = (currentStatus == TorServiceConstants.STATUS_ON); + mTorStatus.notify(); + } + } catch (IllegalMonitorStateException e) { + // |synchronized| should prevent this + } + } + }; + + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(BrowserApp.this); + lbm.registerReceiver(mLocalBroadcastReceiver, + new IntentFilter(TorServiceConstants.ACTION_STATUS)); + + } + }).start(); + + return mLocalBroadcastReceiver; + } + + /** + * Receive the current status from the service. + * The response is sent as a broadcast. If it is not received within + * 1 second, then return false. + * + * This method is coupled with setupReceiveTorIsStartedAsync(). They + * should be used together. + */ + private boolean receiveTorIsStartedAsync(BroadcastReceiver mLocalBroadcastReceiver, Boolean torStatus) { + // Wait until we're notified from the above thread, or we're + // interrupted by the timeout. + try { + // One thousand milliseconds = one second + final long oneSecTimeout = Math.round(Math.pow(10, 3)); + synchronized (torStatus) { + // We wake from wait() because we reached the one second + // timeout, the BroadcastReceiver notified us, or we received + // a spurious wakeup. For all three cases, we can accept the + // current value of torStatus. + torStatus.wait(oneSecTimeout); + } + } catch (InterruptedException e) { + // ignore. + } catch (IllegalArgumentException e) { + // oneSecTimeout should never be negative + } catch (IllegalMonitorStateException e) { + // |synchronized| should take care of this + } + + // Unregister the receiver + LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalBroadcastReceiver); + + return torStatus; + } + + /** + * Receive the current Tor status. + * + * Send a request for the current status and receive the response. + * Returns true if Tor is running, false otherwise. + * + * mTorStatus provides synchronization across threads. + */ + private boolean checkTorIsStarted() { + // When tor is started, true. Otherwise, false + mTorStatus = false; + BroadcastReceiver br = setupReceiveTorIsStartedAsync(); + new Thread(new Runnable() { + @Override + public void run() { + requestTorIsStartedAsync(); + } + }).start(); + + return receiveTorIsStartedAsync(br, mTorStatus); + } + private Class getMediaPlayerManager() { if (AppConstants.MOZ_MEDIA_PLAYER) { try { @@ -1097,6 +1230,13 @@ public class BrowserApp extends GeckoApp for (BrowserAppDelegate delegate : delegates) { delegate.onResume(this); } + + // isInAutomation is overloaded with isTorBrowser(), but here we actually + // need to know if we are in automation. + final SafeIntent intent = new SafeIntent(getIntent()); + if (!IntentUtils.getIsInAutomationFromEnvironment(intent)) { + mTorNeedsStart = !checkTorIsStarted(); + } } @Override @@ -1614,6 +1754,8 @@ public class BrowserApp extends GeckoApp MmaDelegate.flushResources(this); + mTorNeedsStart = true; + super.onDestroy(); } diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java index 026982f50d703..7c8aea696c3aa 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -99,6 +99,8 @@ import org.mozilla.geckoview.GeckoViewBridge; // SafeReceiver excluded at compile-time //import org.mozilla.mozstumbler.service.mainthread.SafeReceiver; +import org.torproject.android.service.TorService; + import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -625,6 +627,9 @@ public abstract class GeckoApp extends GeckoActivity EventDispatcher.getInstance().dispatch("Browser:Quit", res); + Intent torService = new Intent(this, TorService.class); + stopService(torService); + // We don't call shutdown here because this creates a race condition which // can cause the clearing of private data to fail. Instead, we shut down the // UI only after we're done sanitizing. @@ -2215,6 +2220,11 @@ public abstract class GeckoApp extends GeckoActivity GeckoApplication.shutdown(!mRestartOnShutdown ? null : new Intent( Intent.ACTION_MAIN, /* uri */ null, getApplicationContext(), getClass())); } + + if (isFinishing()) { + Log.i(LOGTAG, "onDestroy() is finishing."); + quitAndClear(); + } } public void showSDKVersionError() { diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java index 99630b0bc2d95..bb3cdec7c0fe6 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java @@ -75,6 +75,8 @@ import java.lang.reflect.Method; import java.net.URI; import java.util.UUID; +import org.torproject.android.service.util.Prefs; + public class GeckoApplication extends Application implements HapticFeedbackDelegate, SharedPreferences.OnSharedPreferenceChangeListener { @@ -403,6 +405,9 @@ public class GeckoApplication extends Application "Profile:Create", null); + // Give Orbot the base Context + Prefs.setContext(context); + super.onCreate(); } diff --git a/mobile/android/config/proguard/proguard.cfg b/mobile/android/config/proguard/proguard.cfg index 175ec85518d90..711e66c4bfc6a 100644 --- a/mobile/android/config/proguard/proguard.cfg +++ b/mobile/android/config/proguard/proguard.cfg @@ -170,6 +170,20 @@ -dontwarn java.lang.management.** -dontwarn javax.management.** +# XXX 68rebase Are these still needed? +# From https://github.com/square/okhttp/blob/master/okhttp/src/main/resources/META-INF/proguard/okhttp3.pro +# JSR 305 annotations are for embedding nullability information. +-dontwarn javax.annotation.** + +# A resource is loaded with a relative path so the package of this class must be preserved. +-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase + +# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. +-dontwarn org.codehaus.mojo.animal_sniffer.* + +# OkHttp platform used only on JVM and when Conscrypt dependency is available. +-dontwarn okhttp3.internal.platform.ConscryptPlatform + -include "adjust-keeps.cfg" -include "leakcanary-keeps.cfg" -- GitLab