commit a627afad7b69ff3757fb791921d78281ad94c68b Author: Matthew Finkel Matthew.Finkel@gmail.com Date: Wed Nov 14 17:36:53 2018 +0000
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 ++ .../base/java/org/mozilla/gecko/BrowserApp.java | 142 +++++++++++++++++++++ .../base/java/org/mozilla/gecko/GeckoApp.java | 10 ++ .../java/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 8b91888b5d7f..95dfc2ed1323 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 c6a0bc45d56f..be0ccdb1b13f 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 48809195dc57..c60210e0332c 100644 --- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -572,5 +572,13 @@ <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> #endif
+ <!-- Define Orbotservice's TorService --> + <service + android:name="org.torproject.android.service.TorService" + android:enabled="true" + android:exported="false" + android:stopWithTask="true"> + </service> + </application> </manifest> diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java index 4123acca9bbe..e0ef8e9c43d9 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; @@ -40,6 +42,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; @@ -176,6 +180,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; @@ -251,6 +258,8 @@ public class BrowserApp extends GeckoApp private HomeScreen mHomeScreen; private TabsPanel mTabsPanel;
+ private boolean mTorNeedsStart = true; + private boolean showSplashScreen = false; private SplashScreen splashScreen; /** @@ -1013,6 +1022,130 @@ public class BrowserApp extends GeckoApp .buildAndShow(); }
+ /** + * 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 { @@ -1124,6 +1257,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 @@ -1641,6 +1781,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 c988923e960f..e01318dab422 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -100,6 +100,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; @@ -623,6 +625,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. @@ -2236,6 +2241,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 dbd57d72ebc8..26e06b55ecfc 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java @@ -74,6 +74,8 @@ import java.lang.reflect.Method; import java.net.URL; import java.util.UUID;
+import org.torproject.android.service.util.Prefs; + public class GeckoApplication extends Application implements HapticFeedbackDelegate, SharedPreferences.OnSharedPreferenceChangeListener { @@ -402,6 +404,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 175ec85518d9..711e66c4bfc6 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-... +# 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"
tor-commits@lists.torproject.org