- Tor Cleanup:
- Only starts Tor when the "Internal" option is selected on the privacy settings - Adds a mutex to make sure tor only starts once. - Fixes bug when setting port before the service is running - Moves tor start and stop to Compose actions, instead of activity - Adds a 5 second delay before disconnecting Tor on the App's onPause
This commit is contained in:
@@ -103,10 +103,21 @@ class ServiceManager(
|
||||
if (myAccount != null) {
|
||||
when (myAccount.settings.torSettings.torType.value) {
|
||||
TorType.INTERNAL -> {
|
||||
Log.d("TorManager", "Relays Service Connected ${TorManager.socksPort()}")
|
||||
HttpClientManager.setDefaultProxyOnPort(TorManager.socksPort())
|
||||
// Tor's lib will automatically set this port.
|
||||
if (TorManager.isSocksReady()) {
|
||||
HttpClientManager.setDefaultProxyOnPort(TorManager.socksPort())
|
||||
} else {
|
||||
HttpClientManager.setProxyNotReady()
|
||||
}
|
||||
}
|
||||
TorType.EXTERNAL -> {
|
||||
val port = myAccount.settings.torSettings.externalSocksPort.value
|
||||
if (port > 0) {
|
||||
HttpClientManager.setDefaultProxyOnPort(port)
|
||||
} else {
|
||||
HttpClientManager.setProxyNotReady()
|
||||
}
|
||||
}
|
||||
TorType.EXTERNAL -> HttpClientManager.setDefaultProxyOnPort(myAccount.settings.torSettings.externalSocksPort.value)
|
||||
else -> HttpClientManager.setDefaultProxy(null)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.service.location.LocationState
|
||||
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||
import com.vitorpamplona.amethyst.service.uploads.FileHeader
|
||||
import com.vitorpamplona.amethyst.tryAndWait
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
|
||||
@@ -3648,12 +3647,6 @@ class Account(
|
||||
|
||||
fun markDonatedInThisVersion() = settings.markDonatedInThisVersion(BuildConfig.VERSION_NAME)
|
||||
|
||||
fun httpClientForCoil() = HttpClientManager.getHttpClient(shouldUseTorForImageDownload())
|
||||
|
||||
fun httpClientForRelay(dirtyUrl: String) = HttpClientManager.getHttpClient(shouldUseTorForDirty(dirtyUrl))
|
||||
|
||||
fun httpClientForPreviewUrl(url: String) = HttpClientManager.getHttpClient(shouldUseTorForPreviewUrl(url))
|
||||
|
||||
fun shouldUseTorForImageDownload() =
|
||||
when (settings.torSettings.torType.value) {
|
||||
TorType.OFF -> false
|
||||
|
||||
+10
-2
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.okhttp
|
||||
|
||||
import android.R.attr.port
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.encryption.NostrCipher
|
||||
import okhttp3.OkHttpClient
|
||||
@@ -35,6 +36,8 @@ object HttpClientManager {
|
||||
.followSslRedirects(true)
|
||||
.build()
|
||||
|
||||
val DEFAULT_TOR_PROXY = Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", 9050))
|
||||
|
||||
val DEFAULT_TIMEOUT_ON_WIFI: Duration = Duration.ofSeconds(10L)
|
||||
val DEFAULT_TIMEOUT_ON_MOBILE: Duration = Duration.ofSeconds(30L)
|
||||
|
||||
@@ -43,13 +46,13 @@ object HttpClientManager {
|
||||
private var defaultHttpClientWithoutProxy: OkHttpClient? = null
|
||||
private var userAgent: String = "Amethyst"
|
||||
|
||||
private var currentProxy: Proxy? = null
|
||||
private var currentProxy: Proxy? = DEFAULT_TOR_PROXY
|
||||
|
||||
private val cache = EncryptionKeyCache()
|
||||
|
||||
fun setDefaultProxy(proxy: Proxy?) {
|
||||
if (currentProxy != proxy) {
|
||||
Log.d("HttpClient", "Changing proxy to: ${proxy != null}")
|
||||
Log.d("HttpClient", "Changing proxy to: $proxy")
|
||||
currentProxy = proxy
|
||||
|
||||
// recreates singleton
|
||||
@@ -117,6 +120,11 @@ object HttpClientManager {
|
||||
defaultHttpClientWithoutProxy!!
|
||||
}
|
||||
|
||||
fun setProxyNotReady() {
|
||||
// this blocks all connections unless Orbot is live.
|
||||
setDefaultProxy(DEFAULT_TOR_PROXY)
|
||||
}
|
||||
|
||||
fun setDefaultProxyOnPort(port: Int) {
|
||||
setDefaultProxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", port)))
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.AccountScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
@@ -112,8 +111,6 @@ class MainActivity : AppCompatActivity() {
|
||||
checkLanguage(locales.get(0).language)
|
||||
}
|
||||
|
||||
TorManager.startTor(this)
|
||||
|
||||
Log.d("Lifetime Event", "MainActivity.onResume")
|
||||
|
||||
// starts muted every time
|
||||
@@ -160,8 +157,6 @@ class MainActivity : AppCompatActivity() {
|
||||
(getSystemService(ConnectivityManager::class.java) as ConnectivityManager)
|
||||
.unregisterNetworkCallback(networkCallback)
|
||||
|
||||
TorManager.stopTor(this)
|
||||
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -50,6 +50,7 @@ import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults.contentPadding
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -246,7 +247,9 @@ private fun DialogContent(
|
||||
val writeStoragePermissionState =
|
||||
rememberPermissionState(Manifest.permission.WRITE_EXTERNAL_STORAGE) { isGranted ->
|
||||
if (isGranted) {
|
||||
saveMediaToGallery(myContent, localContext, accountViewModel)
|
||||
scope.launch {
|
||||
saveMediaToGallery(myContent, localContext, accountViewModel)
|
||||
}
|
||||
scope.launch {
|
||||
Toast
|
||||
.makeText(
|
||||
@@ -264,7 +267,9 @@ private fun DialogContent(
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
|
||||
writeStoragePermissionState.status.isGranted
|
||||
) {
|
||||
saveMediaToGallery(myContent, localContext, accountViewModel)
|
||||
scope.launch {
|
||||
saveMediaToGallery(myContent, localContext, accountViewModel)
|
||||
}
|
||||
scope.launch {
|
||||
Toast
|
||||
.makeText(
|
||||
|
||||
@@ -39,13 +39,16 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@@ -58,8 +61,13 @@ import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginOrSignupScreen
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorType
|
||||
import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun AccountScreen(
|
||||
@@ -136,6 +144,8 @@ fun LoggedInPage(
|
||||
accountViewModel.restartServices()
|
||||
}
|
||||
|
||||
ManageTorInstance(accountViewModel)
|
||||
|
||||
ListenToExternalSignerIfNeeded(accountViewModel)
|
||||
|
||||
AppNavigation(
|
||||
@@ -145,6 +155,55 @@ fun LoggedInPage(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ManageTorInstance(accountViewModel: AccountViewModel) {
|
||||
val torSettings by accountViewModel.account.settings.torSettings.torType
|
||||
.collectAsStateWithLifecycle()
|
||||
if (torSettings == TorType.INTERNAL) {
|
||||
ManageTorInstanceInner(accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ManageTorInstanceInner(accountViewModel: AccountViewModel) {
|
||||
val context = LocalContext.current.applicationContext
|
||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
var job = remember<Job?> { null }
|
||||
|
||||
DisposableEffect(key1 = accountViewModel) {
|
||||
job?.cancel()
|
||||
job = null
|
||||
TorManager.startTorIfNotAlreadyOn(context)
|
||||
|
||||
val observer =
|
||||
LifecycleEventObserver { _, event ->
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
job?.cancel()
|
||||
job = null
|
||||
TorManager.startTorIfNotAlreadyOn(context)
|
||||
}
|
||||
Lifecycle.Event.ON_PAUSE -> {
|
||||
job =
|
||||
scope.launch {
|
||||
delay(5000) // 5 seconds
|
||||
TorManager.stopTor(context)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
lifeCycleOwner.lifecycle.addObserver(observer)
|
||||
onDispose {
|
||||
lifeCycleOwner.lifecycle.removeObserver(observer)
|
||||
TorManager.stopTor(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
|
||||
if (accountViewModel.account.signer is NostrSignerExternal) {
|
||||
|
||||
@@ -30,20 +30,40 @@ import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
|
||||
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||
import org.torproject.jni.TorService
|
||||
import org.torproject.jni.TorService.LocalBinder
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
object TorManager {
|
||||
var runningIntent: Intent? = null
|
||||
var torService: TorService? = null
|
||||
|
||||
// To make sure we don't start two services.
|
||||
var isConnectingMutex = AtomicBoolean(false)
|
||||
|
||||
fun startTorIfNotAlreadyOn(ctx: Context) {
|
||||
if (torService == null) {
|
||||
startTor(ctx)
|
||||
if (runningIntent == null && isConnectingMutex.compareAndSet(false, true)) {
|
||||
try {
|
||||
startTor(ctx)
|
||||
} finally {
|
||||
isConnectingMutex.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startTor(ctx: Context) {
|
||||
fun stopTor(ctx: Context) {
|
||||
Log.d("TorManager", "Stopping Tor Service")
|
||||
runningIntent?.let {
|
||||
ctx.stopService(runningIntent)
|
||||
}
|
||||
runningIntent = null
|
||||
torService = null
|
||||
}
|
||||
|
||||
private fun startTor(ctx: Context) {
|
||||
Log.d("TorManager", "Binding Tor Service")
|
||||
val currentIntent = Intent(ctx, TorService::class.java)
|
||||
runningIntent = currentIntent
|
||||
ctx.bindService(
|
||||
Intent(ctx, TorService::class.java),
|
||||
currentIntent,
|
||||
object : ServiceConnection {
|
||||
override fun onServiceConnected(
|
||||
name: ComponentName,
|
||||
@@ -66,6 +86,7 @@ object TorManager {
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName) {
|
||||
runningIntent = null
|
||||
torService = null
|
||||
Log.d("TorManager", "Tor Service Disconected")
|
||||
}
|
||||
@@ -74,12 +95,6 @@ object TorManager {
|
||||
)
|
||||
}
|
||||
|
||||
fun stopTor(ctx: Context) {
|
||||
Log.d("TorManager", "Stopping Tor Service")
|
||||
torService = null
|
||||
ctx.stopService(Intent(ctx, TorService::class.java))
|
||||
}
|
||||
|
||||
fun isSocksReady() =
|
||||
torService?.let {
|
||||
it.socksPort > 0
|
||||
|
||||
Reference in New Issue
Block a user