Loading app/src/main/java/org/mozilla/fenix/HomeActivity.kt +0 −65 Original line number Diff line number Diff line Loading @@ -40,8 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.channels.Channel import mozilla.components.browser.search.SearchEngine import mozilla.components.browser.state.selector.getNormalOrPrivateTabs import mozilla.components.browser.state.state.SessionState Loading Loading @@ -275,63 +273,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { components.appStartupTelemetry.onHomeActivityOnRestart(rootContainer) } /** * Receive the current Tor status. * * Send a request for the current status and receive the response. * Returns true if Tor is running, false otherwise. * */ private suspend fun checkTorIsStarted(): Boolean { val channel = Channel<Boolean>() // Register receiver val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(this@HomeActivity) val localBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action ?: return // We only want ACTION_STATUS messages if (action != TorServiceConstants.ACTION_STATUS) { return } // The current status has the EXTRA_STATUS key val currentStatus = intent.getStringExtra(TorServiceConstants.EXTRA_STATUS) channel.offer(currentStatus === TorServiceConstants.STATUS_ON) } } lbm.registerReceiver( localBroadcastReceiver, IntentFilter(TorServiceConstants.ACTION_STATUS) ) // Request service status val torServiceStatus = Intent(this@HomeActivity, TorService::class.java) torServiceStatus.action = TorServiceConstants.ACTION_STATUS startService(torServiceStatus) // Wait for response and unregister receiver var torIsStarted = false withTimeoutOrNull(timeout) { torIsStarted = channel.receive() } lbm.unregisterReceiver(localBroadcastReceiver) return torIsStarted } @CallSuper override fun onResume() { if (!BuildConfig.DISABLE_TOR) { lifecycleScope.launch { val torNeedsStart = !checkTorIsStarted() if (torNeedsStart) { val torServiceStatus = Intent(this@HomeActivity, TorService::class.java) torServiceStatus.action = TorServiceConstants.ACTION_START startService(torServiceStatus) } } } super.onResume() // Diagnostic breadcrumb for "Display already aquired" crash: Loading Loading @@ -437,13 +380,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { ) privateNotificationObserver?.stop() if (BuildConfig.DISABLE_TOR) { return } val torService = Intent(this, TorService::class.java) stopService(torService) } override fun onConfigurationChanged(newConfig: Configuration) { Loading Loading @@ -933,7 +869,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { const val EXTRA_DELETE_PRIVATE_TABS = "notification_delete_and_open" const val EXTRA_OPENED_FROM_NOTIFICATION = "notification_open" const val START_IN_RECENTS_SCREEN = "start_in_recents_screen" const val timeout = 5000L // PWA must have been used within last 30 days to be considered "recently used" for the // telemetry purposes. Loading app/src/main/java/org/mozilla/fenix/tor/TorController.kt 0 → 100644 +374 −0 Original line number Diff line number Diff line /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.fenix.tor import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import androidx.lifecycle.LifecycleCoroutineScope import androidx.localbroadcastmanager.content.LocalBroadcastManager import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull import org.mozilla.fenix.BuildConfig import org.torproject.android.service.TorService import org.torproject.android.service.TorServiceConstants import org.torproject.android.service.util.Prefs interface TorEvents { fun onTorConnecting() fun onTorConnected() fun onTorStatusUpdate(entry: String?, status: String?) fun onTorStopped() } private enum class TorStatus { OFF, STARTING, ON, STOPPING, UNKNOWN; fun getStateFromString(status: String): TorStatus { return when (status) { TorServiceConstants.STATUS_ON -> ON TorServiceConstants.STATUS_STARTING -> STARTING TorServiceConstants.STATUS_STOPPING -> STOPPING TorServiceConstants.STATUS_OFF -> OFF else -> UNKNOWN } } fun isOff() = this == OFF fun isOn() = this == ON fun isStarting() = this == STARTING fun isStarted() = ((this == TorStatus.STARTING) || (this == TorStatus.ON)) fun isStopping() = this == STOPPING fun isUnknown() = this == UNKNOWN } @SuppressWarnings("TooManyFunctions") class TorController( private val context: Context ) : TorEvents { private val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context) private val entries = mutableListOf<Pair<String?, String?>>() val logEntries get() = entries private var torListeners = mutableListOf<TorEvents>() private var pendingRegisterChangeList = mutableListOf<Pair<TorEvents, Boolean>>() private var lockTorListenersMutation = false private var lastKnownStatus = TorStatus.OFF private var wasTorBootstrapped = false private var isTorRestarting = false // This may be a lie private var isTorBootstrapped = false get() = ((lastKnownStatus == TorStatus.ON) && wasTorBootstrapped) val isDebugLoggingEnabled get() = context .getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE) .getBoolean("pref_enable_logging", false) val isStarting get() = lastKnownStatus.isStarting() val isRestarting get() = isTorRestarting val isBootstrapped get() = isTorBootstrapped val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting) var bridgesEnabled: Boolean get() = Prefs.bridgesEnabled() set(value) { Prefs.putBridgesEnabled(value) } var bridgeTransport: TorBridgeTransportConfig get() { return TorBridgeTransportConfigUtil.getStringToBridgeTransport( Prefs.getBridgesList() ) } set(value) { if (value == TorBridgeTransportConfig.USER_PROVIDED) { // Don't set the pref when the value is USER_PROVIDED because // "user_provided" is not a valid bridge or transport type. // This call should be followed by setting userProvidedBridges. return } Prefs.setBridgesList(value.transportName) } var userProvidedBridges: String? get() { val bridges = Prefs.getBridgesList() val bridgeType = TorBridgeTransportConfigUtil.getStringToBridgeTransport(bridges) return when (bridgeType) { TorBridgeTransportConfig.USER_PROVIDED -> bridges else -> null } } set(value) { Prefs.setBridgesList(value) } fun start() { // Register receiver lbm.registerReceiver( persistentBroadcastReceiver, IntentFilter(TorServiceConstants.ACTION_STATUS) ) lbm.registerReceiver( persistentBroadcastReceiver, IntentFilter(TorServiceConstants.LOCAL_ACTION_LOG) ) } fun stop() { lbm.unregisterReceiver(persistentBroadcastReceiver) } private val persistentBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == null || (intent.action != TorServiceConstants.ACTION_STATUS && intent.action != TorServiceConstants.LOCAL_ACTION_LOG) ) { return } val action = intent.action val logentry: String? val status: String? if (action == TorServiceConstants.LOCAL_ACTION_LOG) { logentry = intent.getExtras() ?.getCharSequence(TorServiceConstants.LOCAL_EXTRA_LOG) as? String? } else { logentry = null } status = intent.getExtras() ?.getCharSequence(TorServiceConstants.EXTRA_STATUS) as? String? if (logentry == null && status == null) { return } onTorStatusUpdate(logentry, status) if (status == null) { return } val newStatus = lastKnownStatus.getStateFromString(status) if (newStatus.isUnknown() && wasTorBootstrapped) { stopTor() } entries.add(Pair(logentry, status)) if (logentry != null && logentry.contains(TorServiceConstants.TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)) { wasTorBootstrapped = true onTorConnected() } if (lastKnownStatus.isStopping() && newStatus.isOff()) { if (isTorRestarting) { initiateTorBootstrap() } else { onTorStopped() } } if (lastKnownStatus.isOff() && newStatus.isStarting()) { isTorRestarting = false } lastKnownStatus = newStatus } } override fun onTorConnecting() { lockTorListenersMutation = true torListeners.forEach { it.onTorConnecting() } lockTorListenersMutation = false handlePendingRegistrationChanges() } override fun onTorConnected() { lockTorListenersMutation = true torListeners.forEach { it.onTorConnected() } lockTorListenersMutation = false handlePendingRegistrationChanges() } override fun onTorStatusUpdate(entry: String?, status: String?) { lockTorListenersMutation = true torListeners.forEach { it.onTorStatusUpdate(entry, status) } lockTorListenersMutation = false handlePendingRegistrationChanges() } override fun onTorStopped() { lockTorListenersMutation = true torListeners.forEach { it.onTorStopped() } lockTorListenersMutation = false handlePendingRegistrationChanges() } fun registerTorListener(l: TorEvents) { if (torListeners.contains(l)) { return } if (lockTorListenersMutation) { pendingRegisterChangeList.add(Pair(l, true)) } else { torListeners.add(l) } } fun unregisterTorListener(l: TorEvents) { if (!torListeners.contains(l)) { return } if (lockTorListenersMutation) { pendingRegisterChangeList.add(Pair(l, false)) } else { torListeners.remove(l) } } private fun handlePendingRegistrationChanges() { pendingRegisterChangeList.forEach { if (it.second) { registerTorListener(it.first) } else { unregisterTorListener(it.first) } } pendingRegisterChangeList.clear() } /** * Receive the current Tor status. * * Send a request for the current status and receive the response. * Returns true if Tor is running, false otherwise. * */ private suspend fun checkTorIsStarted(): Boolean { val channel = Channel<Boolean>() // Register receiver val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context) val localBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action ?: return // We only want ACTION_STATUS messages if (action != TorServiceConstants.ACTION_STATUS) { return } // The current status has the EXTRA_STATUS key val currentStatus = intent.getStringExtra(TorServiceConstants.EXTRA_STATUS) channel.offer(currentStatus === TorServiceConstants.STATUS_ON) } } lbm.registerReceiver( localBroadcastReceiver, IntentFilter(TorServiceConstants.ACTION_STATUS) ) // Request service status sendServiceAction(TorServiceConstants.ACTION_STATUS) // Wait for response and unregister receiver var torIsStarted = false withTimeoutOrNull(torServiceResponseTimeout) { torIsStarted = channel.receive() } lbm.unregisterReceiver(localBroadcastReceiver) return torIsStarted } fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope? = null, withDebugLogging: Boolean = false) { if (BuildConfig.DISABLE_TOR) { return } context.getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE) .edit().putBoolean("pref_enable_logging", withDebugLogging).apply() if (lifecycleScope == null) { sendServiceAction(TorServiceConstants.ACTION_START) } else { lifecycleScope.launch { val torNeedsStart = !checkTorIsStarted() if (torNeedsStart) { sendServiceAction(TorServiceConstants.ACTION_START) } } } } fun stopTor() { if (BuildConfig.DISABLE_TOR) { return } val torService = Intent(context, TorService::class.java) context.stopService(torService) } fun setTorStopped() { lastKnownStatus = TorStatus.OFF onTorStopped() } fun restartTor() { // tor-android-service doesn't dynamically update the torrc file, // and it doesn't use SETCONF, so we completely restart the service. // However, don't restart if we aren't started and we weren't // previously started. if (!lastKnownStatus.isStarted() && !wasTorBootstrapped) { return } if (!lastKnownStatus.isStarted() && wasTorBootstrapped) { // If we aren't started, but we were previously bootstrapped, // then we handle a "restart" request as a "start" restart initiateTorBootstrap() } else { // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor // service. isTorRestarting = true stopTor() } } private fun sendServiceAction(action: String) { val torServiceStatus = Intent(context, TorService::class.java) torServiceStatus.action = action context.startService(torServiceStatus) } companion object { const val torServiceResponseTimeout = 5000L } } Loading
app/src/main/java/org/mozilla/fenix/HomeActivity.kt +0 −65 Original line number Diff line number Diff line Loading @@ -40,8 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.channels.Channel import mozilla.components.browser.search.SearchEngine import mozilla.components.browser.state.selector.getNormalOrPrivateTabs import mozilla.components.browser.state.state.SessionState Loading Loading @@ -275,63 +273,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { components.appStartupTelemetry.onHomeActivityOnRestart(rootContainer) } /** * Receive the current Tor status. * * Send a request for the current status and receive the response. * Returns true if Tor is running, false otherwise. * */ private suspend fun checkTorIsStarted(): Boolean { val channel = Channel<Boolean>() // Register receiver val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(this@HomeActivity) val localBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action ?: return // We only want ACTION_STATUS messages if (action != TorServiceConstants.ACTION_STATUS) { return } // The current status has the EXTRA_STATUS key val currentStatus = intent.getStringExtra(TorServiceConstants.EXTRA_STATUS) channel.offer(currentStatus === TorServiceConstants.STATUS_ON) } } lbm.registerReceiver( localBroadcastReceiver, IntentFilter(TorServiceConstants.ACTION_STATUS) ) // Request service status val torServiceStatus = Intent(this@HomeActivity, TorService::class.java) torServiceStatus.action = TorServiceConstants.ACTION_STATUS startService(torServiceStatus) // Wait for response and unregister receiver var torIsStarted = false withTimeoutOrNull(timeout) { torIsStarted = channel.receive() } lbm.unregisterReceiver(localBroadcastReceiver) return torIsStarted } @CallSuper override fun onResume() { if (!BuildConfig.DISABLE_TOR) { lifecycleScope.launch { val torNeedsStart = !checkTorIsStarted() if (torNeedsStart) { val torServiceStatus = Intent(this@HomeActivity, TorService::class.java) torServiceStatus.action = TorServiceConstants.ACTION_START startService(torServiceStatus) } } } super.onResume() // Diagnostic breadcrumb for "Display already aquired" crash: Loading Loading @@ -437,13 +380,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { ) privateNotificationObserver?.stop() if (BuildConfig.DISABLE_TOR) { return } val torService = Intent(this, TorService::class.java) stopService(torService) } override fun onConfigurationChanged(newConfig: Configuration) { Loading Loading @@ -933,7 +869,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { const val EXTRA_DELETE_PRIVATE_TABS = "notification_delete_and_open" const val EXTRA_OPENED_FROM_NOTIFICATION = "notification_open" const val START_IN_RECENTS_SCREEN = "start_in_recents_screen" const val timeout = 5000L // PWA must have been used within last 30 days to be considered "recently used" for the // telemetry purposes. Loading
app/src/main/java/org/mozilla/fenix/tor/TorController.kt 0 → 100644 +374 −0 Original line number Diff line number Diff line /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.fenix.tor import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import androidx.lifecycle.LifecycleCoroutineScope import androidx.localbroadcastmanager.content.LocalBroadcastManager import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull import org.mozilla.fenix.BuildConfig import org.torproject.android.service.TorService import org.torproject.android.service.TorServiceConstants import org.torproject.android.service.util.Prefs interface TorEvents { fun onTorConnecting() fun onTorConnected() fun onTorStatusUpdate(entry: String?, status: String?) fun onTorStopped() } private enum class TorStatus { OFF, STARTING, ON, STOPPING, UNKNOWN; fun getStateFromString(status: String): TorStatus { return when (status) { TorServiceConstants.STATUS_ON -> ON TorServiceConstants.STATUS_STARTING -> STARTING TorServiceConstants.STATUS_STOPPING -> STOPPING TorServiceConstants.STATUS_OFF -> OFF else -> UNKNOWN } } fun isOff() = this == OFF fun isOn() = this == ON fun isStarting() = this == STARTING fun isStarted() = ((this == TorStatus.STARTING) || (this == TorStatus.ON)) fun isStopping() = this == STOPPING fun isUnknown() = this == UNKNOWN } @SuppressWarnings("TooManyFunctions") class TorController( private val context: Context ) : TorEvents { private val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context) private val entries = mutableListOf<Pair<String?, String?>>() val logEntries get() = entries private var torListeners = mutableListOf<TorEvents>() private var pendingRegisterChangeList = mutableListOf<Pair<TorEvents, Boolean>>() private var lockTorListenersMutation = false private var lastKnownStatus = TorStatus.OFF private var wasTorBootstrapped = false private var isTorRestarting = false // This may be a lie private var isTorBootstrapped = false get() = ((lastKnownStatus == TorStatus.ON) && wasTorBootstrapped) val isDebugLoggingEnabled get() = context .getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE) .getBoolean("pref_enable_logging", false) val isStarting get() = lastKnownStatus.isStarting() val isRestarting get() = isTorRestarting val isBootstrapped get() = isTorBootstrapped val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting) var bridgesEnabled: Boolean get() = Prefs.bridgesEnabled() set(value) { Prefs.putBridgesEnabled(value) } var bridgeTransport: TorBridgeTransportConfig get() { return TorBridgeTransportConfigUtil.getStringToBridgeTransport( Prefs.getBridgesList() ) } set(value) { if (value == TorBridgeTransportConfig.USER_PROVIDED) { // Don't set the pref when the value is USER_PROVIDED because // "user_provided" is not a valid bridge or transport type. // This call should be followed by setting userProvidedBridges. return } Prefs.setBridgesList(value.transportName) } var userProvidedBridges: String? get() { val bridges = Prefs.getBridgesList() val bridgeType = TorBridgeTransportConfigUtil.getStringToBridgeTransport(bridges) return when (bridgeType) { TorBridgeTransportConfig.USER_PROVIDED -> bridges else -> null } } set(value) { Prefs.setBridgesList(value) } fun start() { // Register receiver lbm.registerReceiver( persistentBroadcastReceiver, IntentFilter(TorServiceConstants.ACTION_STATUS) ) lbm.registerReceiver( persistentBroadcastReceiver, IntentFilter(TorServiceConstants.LOCAL_ACTION_LOG) ) } fun stop() { lbm.unregisterReceiver(persistentBroadcastReceiver) } private val persistentBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == null || (intent.action != TorServiceConstants.ACTION_STATUS && intent.action != TorServiceConstants.LOCAL_ACTION_LOG) ) { return } val action = intent.action val logentry: String? val status: String? if (action == TorServiceConstants.LOCAL_ACTION_LOG) { logentry = intent.getExtras() ?.getCharSequence(TorServiceConstants.LOCAL_EXTRA_LOG) as? String? } else { logentry = null } status = intent.getExtras() ?.getCharSequence(TorServiceConstants.EXTRA_STATUS) as? String? if (logentry == null && status == null) { return } onTorStatusUpdate(logentry, status) if (status == null) { return } val newStatus = lastKnownStatus.getStateFromString(status) if (newStatus.isUnknown() && wasTorBootstrapped) { stopTor() } entries.add(Pair(logentry, status)) if (logentry != null && logentry.contains(TorServiceConstants.TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)) { wasTorBootstrapped = true onTorConnected() } if (lastKnownStatus.isStopping() && newStatus.isOff()) { if (isTorRestarting) { initiateTorBootstrap() } else { onTorStopped() } } if (lastKnownStatus.isOff() && newStatus.isStarting()) { isTorRestarting = false } lastKnownStatus = newStatus } } override fun onTorConnecting() { lockTorListenersMutation = true torListeners.forEach { it.onTorConnecting() } lockTorListenersMutation = false handlePendingRegistrationChanges() } override fun onTorConnected() { lockTorListenersMutation = true torListeners.forEach { it.onTorConnected() } lockTorListenersMutation = false handlePendingRegistrationChanges() } override fun onTorStatusUpdate(entry: String?, status: String?) { lockTorListenersMutation = true torListeners.forEach { it.onTorStatusUpdate(entry, status) } lockTorListenersMutation = false handlePendingRegistrationChanges() } override fun onTorStopped() { lockTorListenersMutation = true torListeners.forEach { it.onTorStopped() } lockTorListenersMutation = false handlePendingRegistrationChanges() } fun registerTorListener(l: TorEvents) { if (torListeners.contains(l)) { return } if (lockTorListenersMutation) { pendingRegisterChangeList.add(Pair(l, true)) } else { torListeners.add(l) } } fun unregisterTorListener(l: TorEvents) { if (!torListeners.contains(l)) { return } if (lockTorListenersMutation) { pendingRegisterChangeList.add(Pair(l, false)) } else { torListeners.remove(l) } } private fun handlePendingRegistrationChanges() { pendingRegisterChangeList.forEach { if (it.second) { registerTorListener(it.first) } else { unregisterTorListener(it.first) } } pendingRegisterChangeList.clear() } /** * Receive the current Tor status. * * Send a request for the current status and receive the response. * Returns true if Tor is running, false otherwise. * */ private suspend fun checkTorIsStarted(): Boolean { val channel = Channel<Boolean>() // Register receiver val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context) val localBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action ?: return // We only want ACTION_STATUS messages if (action != TorServiceConstants.ACTION_STATUS) { return } // The current status has the EXTRA_STATUS key val currentStatus = intent.getStringExtra(TorServiceConstants.EXTRA_STATUS) channel.offer(currentStatus === TorServiceConstants.STATUS_ON) } } lbm.registerReceiver( localBroadcastReceiver, IntentFilter(TorServiceConstants.ACTION_STATUS) ) // Request service status sendServiceAction(TorServiceConstants.ACTION_STATUS) // Wait for response and unregister receiver var torIsStarted = false withTimeoutOrNull(torServiceResponseTimeout) { torIsStarted = channel.receive() } lbm.unregisterReceiver(localBroadcastReceiver) return torIsStarted } fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope? = null, withDebugLogging: Boolean = false) { if (BuildConfig.DISABLE_TOR) { return } context.getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE) .edit().putBoolean("pref_enable_logging", withDebugLogging).apply() if (lifecycleScope == null) { sendServiceAction(TorServiceConstants.ACTION_START) } else { lifecycleScope.launch { val torNeedsStart = !checkTorIsStarted() if (torNeedsStart) { sendServiceAction(TorServiceConstants.ACTION_START) } } } } fun stopTor() { if (BuildConfig.DISABLE_TOR) { return } val torService = Intent(context, TorService::class.java) context.stopService(torService) } fun setTorStopped() { lastKnownStatus = TorStatus.OFF onTorStopped() } fun restartTor() { // tor-android-service doesn't dynamically update the torrc file, // and it doesn't use SETCONF, so we completely restart the service. // However, don't restart if we aren't started and we weren't // previously started. if (!lastKnownStatus.isStarted() && !wasTorBootstrapped) { return } if (!lastKnownStatus.isStarted() && wasTorBootstrapped) { // If we aren't started, but we were previously bootstrapped, // then we handle a "restart" request as a "start" restart initiateTorBootstrap() } else { // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor // service. isTorRestarting = true stopTor() } } private fun sendServiceAction(action: String) { val torServiceStatus = Intent(context, TorService::class.java) torServiceStatus.action = action context.startService(torServiceStatus) } companion object { const val torServiceResponseTimeout = 5000L } }