Dan Ballard pushed to branch firefox-android-115.2.1-13.5-1 at The Tor Project / Applications / firefox-android

Commits:

4 changed files:

Changes:

  • fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt
    ... ... @@ -43,7 +43,8 @@ import org.mozilla.fenix.perf.StartupActivityLog
    43 43
     import org.mozilla.fenix.perf.StartupStateProvider
    
    44 44
     import org.mozilla.fenix.perf.StrictModeManager
    
    45 45
     import org.mozilla.fenix.perf.lazyMonitored
    
    46
    -import org.mozilla.fenix.tor.TorController
    
    46
    +import org.mozilla.fenix.tor.TorControllerGV
    
    47
    +import org.mozilla.fenix.tor.TorControllerTAS
    
    47 48
     import org.mozilla.fenix.utils.ClipboardHandler
    
    48 49
     import org.mozilla.fenix.utils.Settings
    
    49 50
     import org.mozilla.fenix.wifi.WifiConnectionMonitor
    
    ... ... @@ -201,7 +202,7 @@ class Components(private val context: Context) {
    201 202
                 ),
    
    202 203
             )
    
    203 204
         }
    
    204
    -    val torController by lazyMonitored { TorController(context) }
    
    205
    +    val torController by lazyMonitored { if (settings.useNewBootstrap) TorControllerGV(context) else TorControllerTAS(context) }
    
    205 206
     }
    
    206 207
     
    
    207 208
     /**
    

  • fenix/app/src/main/java/org/mozilla/fenix/tor/TorController.kt
    ... ... @@ -4,22 +4,7 @@
    4 4
     
    
    5 5
     package org.mozilla.fenix.tor
    
    6 6
     
    
    7
    -import android.content.BroadcastReceiver
    
    8
    -import android.content.Context
    
    9
    -import android.content.Intent
    
    10
    -import android.content.IntentFilter
    
    11 7
     import androidx.lifecycle.LifecycleCoroutineScope
    
    12
    -import androidx.localbroadcastmanager.content.LocalBroadcastManager
    
    13
    -
    
    14
    -import kotlinx.coroutines.channels.Channel
    
    15
    -import kotlinx.coroutines.launch
    
    16
    -import kotlinx.coroutines.withTimeoutOrNull
    
    17
    -
    
    18
    -import org.mozilla.fenix.BuildConfig
    
    19
    -
    
    20
    -import org.torproject.android.service.TorService
    
    21
    -import org.torproject.android.service.TorServiceConstants
    
    22
    -import org.torproject.android.service.util.Prefs
    
    23 8
     
    
    24 9
     interface TorEvents {
    
    25 10
         fun onTorConnecting()
    
    ... ... @@ -28,347 +13,59 @@ interface TorEvents {
    28 13
         fun onTorStopped()
    
    29 14
     }
    
    30 15
     
    
    31
    -private enum class TorStatus {
    
    32
    -    OFF,
    
    33
    -    STARTING,
    
    34
    -    ON,
    
    35
    -    STOPPING,
    
    36
    -    UNKNOWN;
    
    16
    +internal enum class TorStatus(val status: String) {
    
    17
    +    OFF("OFF"),
    
    18
    +    STARTING("STARTING"),
    
    19
    +    ON("ON"),
    
    20
    +    STOPPING("STOPPING"),
    
    21
    +    UNKNOWN("UNKNOWN");
    
    37 22
     
    
    38
    -    fun getStateFromString(status: String): TorStatus {
    
    39
    -        return when (status) {
    
    40
    -            TorServiceConstants.STATUS_ON -> ON
    
    41
    -            TorServiceConstants.STATUS_STARTING -> STARTING
    
    42
    -            TorServiceConstants.STATUS_STOPPING -> STOPPING
    
    43
    -            TorServiceConstants.STATUS_OFF -> OFF
    
    44
    -            else -> UNKNOWN
    
    23
    +    companion object {
    
    24
    +        fun fromString(status: String): TorStatus {
    
    25
    +            return when (status) {
    
    26
    +                "ON" -> ON
    
    27
    +                "STARTING" -> STARTING
    
    28
    +                "STOPPING" -> STOPPING
    
    29
    +                "OFF" -> OFF
    
    30
    +                else -> UNKNOWN
    
    31
    +            }
    
    45 32
             }
    
    46 33
         }
    
    47 34
     
    
    48 35
         fun isOff() = this == OFF
    
    49 36
         fun isOn() = this == ON
    
    50 37
         fun isStarting() = this == STARTING
    
    51
    -    fun isStarted() = ((this == TorStatus.STARTING) || (this == TorStatus.ON))
    
    38
    +    fun isStarted() = ((this == STARTING) || (this == ON))
    
    52 39
         fun isStopping() = this == STOPPING
    
    53 40
         fun isUnknown() = this == UNKNOWN
    
    54 41
     }
    
    55 42
     
    
    56
    -@SuppressWarnings("TooManyFunctions")
    
    57
    -class TorController(
    
    58
    -    private val context: Context
    
    59
    -) : TorEvents {
    
    60
    -
    
    61
    -    private val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
    
    62
    -    private val entries = mutableListOf<Pair<String?, String?>>()
    
    63
    -    val logEntries get() = entries
    
    64
    -
    
    65
    -    private var torListeners = mutableListOf<TorEvents>()
    
    66
    -
    
    67
    -    private var pendingRegisterChangeList = mutableListOf<Pair<TorEvents, Boolean>>()
    
    68
    -    private var lockTorListenersMutation = false
    
    69
    -
    
    70
    -    private var lastKnownStatus = TorStatus.OFF
    
    71
    -    private var wasTorBootstrapped = false
    
    72
    -    private var isTorRestarting = false
    
    73
    -
    
    74
    -    // This may be a lie
    
    75
    -    private var isTorBootstrapped = false
    
    76
    -        get() = ((lastKnownStatus == TorStatus.ON) && wasTorBootstrapped)
    
    77
    -
    
    78
    -    val isDebugLoggingEnabled get() =
    
    79
    -        context
    
    80
    -        .getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
    
    81
    -        .getBoolean("pref_enable_logging", false)
    
    82
    -
    
    83
    -    val isStarting get() = lastKnownStatus.isStarting()
    
    84
    -    val isRestarting get() = isTorRestarting
    
    85
    -    val isBootstrapped get() = isTorBootstrapped
    
    86
    -    val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting)
    
    87
    -
    
    43
    +interface TorController: TorEvents {
    
    44
    +    val logEntries: MutableList<Pair<String?, String?>>
    
    45
    +    val isStarting: Boolean
    
    46
    +    val isRestarting: Boolean
    
    47
    +    val isBootstrapped: Boolean
    
    48
    +    val isConnected: Boolean
    
    88 49
         var bridgesEnabled: Boolean
    
    89
    -        get() = Prefs.bridgesEnabled()
    
    90
    -        set(value) { Prefs.putBridgesEnabled(value) }
    
    91
    -
    
    92 50
         var bridgeTransport: TorBridgeTransportConfig
    
    93
    -        get() {
    
    94
    -            return TorBridgeTransportConfigUtil.getStringToBridgeTransport(
    
    95
    -                Prefs.getBridgesList()
    
    96
    -            )
    
    97
    -        }
    
    98
    -        set(value) {
    
    99
    -            if (value == TorBridgeTransportConfig.USER_PROVIDED) {
    
    100
    -                // Don't set the pref when the value is USER_PROVIDED because
    
    101
    -                // "user_provided" is not a valid bridge or transport type.
    
    102
    -                // This call should be followed by setting userProvidedBridges.
    
    103
    -                return
    
    104
    -            }
    
    105
    -            Prefs.setBridgesList(value.transportName)
    
    106
    -        }
    
    107
    -
    
    108 51
         var userProvidedBridges: String?
    
    109
    -        get() {
    
    110
    -            val bridges = Prefs.getBridgesList()
    
    111
    -            val bridgeType =
    
    112
    -                TorBridgeTransportConfigUtil.getStringToBridgeTransport(bridges)
    
    113
    -            return when (bridgeType) {
    
    114
    -                TorBridgeTransportConfig.USER_PROVIDED -> bridges
    
    115
    -                else -> null
    
    116
    -            }
    
    117
    -        }
    
    118
    -        set(value) {
    
    119
    -            Prefs.setBridgesList(value)
    
    120
    -        }
    
    121
    -
    
    122
    -    fun start() {
    
    123
    -        // Register receiver
    
    124
    -        lbm.registerReceiver(
    
    125
    -            persistentBroadcastReceiver,
    
    126
    -            IntentFilter(TorServiceConstants.ACTION_STATUS)
    
    127
    -        )
    
    128
    -        lbm.registerReceiver(
    
    129
    -            persistentBroadcastReceiver,
    
    130
    -            IntentFilter(TorServiceConstants.LOCAL_ACTION_LOG)
    
    131
    -        )
    
    132
    -    }
    
    133 52
     
    
    134
    -    fun stop() {
    
    135
    -        lbm.unregisterReceiver(persistentBroadcastReceiver)
    
    136
    -    }
    
    53
    +    fun start()
    
    54
    +    fun stop()
    
    137 55
     
    
    138
    -    private val persistentBroadcastReceiver = object : BroadcastReceiver() {
    
    139
    -        override fun onReceive(context: Context, intent: Intent) {
    
    140
    -            if (intent.action == null ||
    
    141
    -                (intent.action != TorServiceConstants.ACTION_STATUS &&
    
    142
    -                intent.action != TorServiceConstants.LOCAL_ACTION_LOG)
    
    143
    -            ) {
    
    144
    -                    return
    
    145
    -            }
    
    146
    -            val action = intent.action
    
    56
    +    override fun onTorConnecting()
    
    57
    +    override fun onTorConnected()
    
    58
    +    override fun onTorStatusUpdate(entry: String?, status: String?)
    
    59
    +    override fun onTorStopped()
    
    147 60
     
    
    148
    -            val logentry: String?
    
    149
    -            val status: String?
    
    150
    -            if (action == TorServiceConstants.LOCAL_ACTION_LOG) {
    
    151
    -                logentry = intent.getExtras()
    
    152
    -                    ?.getCharSequence(TorServiceConstants.LOCAL_EXTRA_LOG) as? String?
    
    153
    -            } else {
    
    154
    -                logentry = null
    
    155
    -            }
    
    156
    -
    
    157
    -            status = intent.getExtras()
    
    158
    -                ?.getCharSequence(TorServiceConstants.EXTRA_STATUS) as? String?
    
    159
    -
    
    160
    -            if (logentry == null && status == null) {
    
    161
    -                return
    
    162
    -            }
    
    163
    -
    
    164
    -            onTorStatusUpdate(logentry, status)
    
    165
    -
    
    166
    -            if (status == null) {
    
    167
    -                return
    
    168
    -            }
    
    169
    -
    
    170
    -            val newStatus = lastKnownStatus.getStateFromString(status)
    
    171
    -
    
    172
    -            if (newStatus.isUnknown() && wasTorBootstrapped) {
    
    173
    -                stopTor()
    
    174
    -            }
    
    175
    -
    
    176
    -            entries.add(Pair(logentry, status))
    
    177
    -
    
    178
    -            if (logentry != null && logentry.contains(TorServiceConstants.TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)) {
    
    179
    -                wasTorBootstrapped = true
    
    180
    -                onTorConnected()
    
    181
    -            }
    
    182
    -
    
    183
    -            if (lastKnownStatus.isStopping() && newStatus.isOff()) {
    
    184
    -                if (isTorRestarting) {
    
    185
    -                    initiateTorBootstrap()
    
    186
    -                } else {
    
    187
    -                    onTorStopped()
    
    188
    -                }
    
    189
    -            }
    
    190
    -
    
    191
    -            if (lastKnownStatus.isOff() && newStatus.isStarting()) {
    
    192
    -                isTorRestarting = false
    
    193
    -            }
    
    194
    -
    
    195
    -            lastKnownStatus = newStatus
    
    196
    -        }
    
    197
    -    }
    
    198
    -
    
    199
    -    override fun onTorConnecting() {
    
    200
    -        lockTorListenersMutation = true
    
    201
    -        torListeners.forEach { it.onTorConnecting() }
    
    202
    -        lockTorListenersMutation = false
    
    203
    -
    
    204
    -        handlePendingRegistrationChanges()
    
    205
    -    }
    
    206
    -
    
    207
    -    override fun onTorConnected() {
    
    208
    -        lockTorListenersMutation = true
    
    209
    -        torListeners.forEach { it.onTorConnected() }
    
    210
    -        lockTorListenersMutation = false
    
    211
    -
    
    212
    -        handlePendingRegistrationChanges()
    
    213
    -    }
    
    61
    +    fun registerTorListener(l: TorEvents)
    
    62
    +    fun unregisterTorListener(l: TorEvents)
    
    214 63
     
    
    215
    -    override fun onTorStatusUpdate(entry: String?, status: String?) {
    
    216
    -        lockTorListenersMutation = true
    
    217
    -        torListeners.forEach { it.onTorStatusUpdate(entry, status) }
    
    218
    -        lockTorListenersMutation = false
    
    219
    -
    
    220
    -        handlePendingRegistrationChanges()
    
    221
    -    }
    
    222
    -
    
    223
    -    override fun onTorStopped() {
    
    224
    -        lockTorListenersMutation = true
    
    225
    -        torListeners.forEach { it.onTorStopped() }
    
    226
    -        lockTorListenersMutation = false
    
    227
    -
    
    228
    -        handlePendingRegistrationChanges()
    
    229
    -    }
    
    230
    -
    
    231
    -    fun registerTorListener(l: TorEvents) {
    
    232
    -        if (torListeners.contains(l)) {
    
    233
    -            return
    
    234
    -        }
    
    235
    -
    
    236
    -        if (lockTorListenersMutation) {
    
    237
    -            pendingRegisterChangeList.add(Pair(l, true))
    
    238
    -        } else {
    
    239
    -            torListeners.add(l)
    
    240
    -        }
    
    241
    -    }
    
    242
    -
    
    243
    -    fun unregisterTorListener(l: TorEvents) {
    
    244
    -        if (!torListeners.contains(l)) {
    
    245
    -            return
    
    246
    -        }
    
    247
    -
    
    248
    -        if (lockTorListenersMutation) {
    
    249
    -            pendingRegisterChangeList.add(Pair(l, false))
    
    250
    -        } else {
    
    251
    -            torListeners.remove(l)
    
    252
    -        }
    
    253
    -    }
    
    254
    -
    
    255
    -    private fun handlePendingRegistrationChanges() {
    
    256
    -        pendingRegisterChangeList.forEach {
    
    257
    -            if (it.second) {
    
    258
    -                registerTorListener(it.first)
    
    259
    -            } else {
    
    260
    -                unregisterTorListener(it.first)
    
    261
    -            }
    
    262
    -        }
    
    263
    -
    
    264
    -        pendingRegisterChangeList.clear()
    
    265
    -    }
    
    266
    -
    
    267
    -    /**
    
    268
    -     * Receive the current Tor status.
    
    269
    -     *
    
    270
    -     * Send a request for the current status and receive the response.
    
    271
    -     * Returns true if Tor is running, false otherwise.
    
    272
    -     *
    
    273
    -     */
    
    274
    -    private suspend fun checkTorIsStarted(): Boolean {
    
    275
    -        val channel = Channel<Boolean>()
    
    276
    -
    
    277
    -        // Register receiver
    
    278
    -        val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
    
    279
    -        val localBroadcastReceiver = object : BroadcastReceiver() {
    
    280
    -            override fun onReceive(context: Context, intent: Intent) {
    
    281
    -                val action = intent.action ?: return
    
    282
    -                // We only want ACTION_STATUS messages
    
    283
    -                if (action != TorServiceConstants.ACTION_STATUS) {
    
    284
    -                    return
    
    285
    -                }
    
    286
    -                // The current status has the EXTRA_STATUS key
    
    287
    -                val currentStatus =
    
    288
    -                    intent.getStringExtra(TorServiceConstants.EXTRA_STATUS)
    
    289
    -                channel.trySend(currentStatus === TorServiceConstants.STATUS_ON)
    
    290
    -            }
    
    291
    -        }
    
    292
    -        lbm.registerReceiver(
    
    293
    -            localBroadcastReceiver,
    
    294
    -            IntentFilter(TorServiceConstants.ACTION_STATUS)
    
    295
    -        )
    
    296
    -
    
    297
    -        // Request service status
    
    298
    -        sendServiceAction(TorServiceConstants.ACTION_STATUS)
    
    299
    -
    
    300
    -        // Wait for response and unregister receiver
    
    301
    -        var torIsStarted = false
    
    302
    -        withTimeoutOrNull(torServiceResponseTimeout) {
    
    303
    -            torIsStarted = channel.receive()
    
    304
    -        }
    
    305
    -        lbm.unregisterReceiver(localBroadcastReceiver)
    
    306
    -        return torIsStarted
    
    307
    -    }
    
    308
    -
    
    309
    -    fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope? = null, withDebugLogging: Boolean = false) {
    
    310
    -        if (BuildConfig.DISABLE_TOR) {
    
    311
    -            return
    
    312
    -        }
    
    313
    -
    
    314
    -        context.getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
    
    315
    -            .edit().putBoolean("pref_enable_logging", withDebugLogging).apply()
    
    316
    -
    
    317
    -        if (lifecycleScope == null) {
    
    318
    -            sendServiceAction(TorServiceConstants.ACTION_START)
    
    319
    -        } else {
    
    320
    -            lifecycleScope.launch {
    
    321
    -                val torNeedsStart = !checkTorIsStarted()
    
    322
    -                if (torNeedsStart) {
    
    323
    -                    sendServiceAction(TorServiceConstants.ACTION_START)
    
    324
    -                }
    
    325
    -            }
    
    326
    -        }
    
    327
    -    }
    
    328
    -
    
    329
    -    fun stopTor() {
    
    330
    -        if (BuildConfig.DISABLE_TOR) {
    
    331
    -            return
    
    332
    -        }
    
    333
    -
    
    334
    -        val torService = Intent(context, TorService::class.java)
    
    335
    -        context.stopService(torService)
    
    336
    -    }
    
    337
    -
    
    338
    -    fun setTorStopped() {
    
    339
    -        lastKnownStatus = TorStatus.OFF
    
    340
    -        onTorStopped()
    
    341
    -    }
    
    342
    -
    
    343
    -    fun restartTor() {
    
    344
    -        // tor-android-service doesn't dynamically update the torrc file,
    
    345
    -        // and it doesn't use SETCONF, so we completely restart the service.
    
    346
    -        // However, don't restart if we aren't started and we weren't
    
    347
    -        // previously started.
    
    348
    -        if (!lastKnownStatus.isStarted() && !wasTorBootstrapped) {
    
    349
    -            return
    
    350
    -        }
    
    64
    +    fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope? = null, withDebugLogging: Boolean = false)
    
    65
    +    fun stopTor()
    
    66
    +    fun setTorStopped()
    
    67
    +    fun restartTor()
    
    68
    +}
    
    351 69
     
    
    352
    -        if (!lastKnownStatus.isStarted() && wasTorBootstrapped) {
    
    353
    -            // If we aren't started, but we were previously bootstrapped,
    
    354
    -            // then we handle a "restart" request as a "start" restart
    
    355
    -            initiateTorBootstrap()
    
    356
    -        } else {
    
    357
    -            // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state
    
    358
    -            // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor
    
    359
    -            // service.
    
    360
    -            isTorRestarting = true
    
    361
    -            stopTor()
    
    362
    -        }
    
    363
    -    }
    
    364 70
     
    
    365
    -    private fun sendServiceAction(action: String) {
    
    366
    -        val torServiceStatus = Intent(context, TorService::class.java)
    
    367
    -        torServiceStatus.action = action
    
    368
    -        context.startService(torServiceStatus)
    
    369
    -    }
    
    370 71
     
    371
    -    companion object {
    
    372
    -        const val torServiceResponseTimeout = 5000L
    
    373
    -    }
    
    374
    -}

  • fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerGV.kt
    1
    +package org.mozilla.fenix.tor
    
    2
    +
    
    3
    +
    
    4
    +import android.content.Context
    
    5
    +import android.util.Log
    
    6
    +import androidx.lifecycle.LifecycleCoroutineScope
    
    7
    +import mozilla.components.browser.engine.gecko.GeckoEngine
    
    8
    +import org.mozilla.fenix.ext.components
    
    9
    +import org.mozilla.geckoview.TorIntegrationAndroid
    
    10
    +import org.mozilla.geckoview.TorIntegrationAndroid.BootstrapStateChangeListener
    
    11
    +import org.mozilla.geckoview.TorSettings
    
    12
    +import org.mozilla.geckoview.TorSettings.BridgeBuiltinType
    
    13
    +import org.mozilla.geckoview.TorSettings.BridgeSource
    
    14
    +
    
    15
    +// Enum matching TorConnectState from TorConnect.sys.mjs that we get from onBootstrapStateChange
    
    16
    +internal enum class TorConnectState(val state: String) {
    
    17
    +    Initial("Initial"),
    
    18
    +    Configuring("Configuring"),
    
    19
    +    AutoBootstrapping("AutoBootstrapping"),
    
    20
    +    Bootstrapping("Bootstrapping"),
    
    21
    +    Error("Error"),
    
    22
    +    Bootstrapped("Bootstrapped"),
    
    23
    +    Disabled("Disabled");
    
    24
    +
    
    25
    +    fun isStarting() = this == Bootstrapping || this == AutoBootstrapping
    
    26
    +    fun isError() = this == Error
    
    27
    +
    
    28
    +    fun isStarted() = this == Bootstrapped
    
    29
    +
    
    30
    +    fun isOff() = this == Initial || this == Configuring || this == Disabled || this == Error
    
    31
    +
    
    32
    +
    
    33
    +    // Convert to TorStatus that firefox-android uses based on tor-android-service
    
    34
    +    fun toTorStatus(): TorStatus {
    
    35
    +        return when (this) {
    
    36
    +            Initial -> TorStatus.OFF
    
    37
    +            Configuring -> TorStatus.OFF
    
    38
    +            AutoBootstrapping -> TorStatus.STARTING
    
    39
    +            Bootstrapping -> TorStatus.STARTING
    
    40
    +            Error -> TorStatus.UNKNOWN
    
    41
    +            Bootstrapped -> TorStatus.ON
    
    42
    +            Disabled -> TorStatus.OFF
    
    43
    +        }
    
    44
    +    }
    
    45
    +}
    
    46
    +
    
    47
    +class TorControllerGV(
    
    48
    +    private val context: Context,
    
    49
    +) : TorController, TorEvents, BootstrapStateChangeListener {
    
    50
    +
    
    51
    +    private val TAG = "TorControllerGV"
    
    52
    +
    
    53
    +    private var torListeners = mutableListOf<TorEvents>()
    
    54
    +
    
    55
    +    private var lastKnownStatus = TorConnectState.Initial
    
    56
    +    private var wasTorBootstrapped = false
    
    57
    +    private var isTorRestarting = false
    
    58
    +
    
    59
    +    private var isTorBootstrapped = false
    
    60
    +        get() = ((lastKnownStatus.isStarted()) && wasTorBootstrapped)
    
    61
    +
    
    62
    +    private val entries = mutableListOf<Pair<String?, String?>>()
    
    63
    +    override val logEntries get() = entries
    
    64
    +    override val isStarting get() = lastKnownStatus.isStarting()
    
    65
    +    override val isRestarting get() = isTorRestarting
    
    66
    +    override val isBootstrapped get() = isTorBootstrapped
    
    67
    +    override val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting)
    
    68
    +
    
    69
    +    private fun getTorIntegration(): TorIntegrationAndroid {
    
    70
    +        return (context.components.core.engine as GeckoEngine).getTorIntegrationController()
    
    71
    +    }
    
    72
    +
    
    73
    +    private fun getTorSettings(): TorSettings? {
    
    74
    +        return getTorIntegration().getSettings()
    
    75
    +    }
    
    76
    +
    
    77
    +    override var bridgesEnabled: Boolean
    
    78
    +        get() {
    
    79
    +            return getTorSettings()?.bridgesEnabled ?: false
    
    80
    +        }
    
    81
    +        set(value) {
    
    82
    +            getTorSettings()?.let {
    
    83
    +                it.bridgesEnabled = value
    
    84
    +                getTorIntegration().setSettings(it, true, true)
    
    85
    +            }
    
    86
    +        }
    
    87
    +
    
    88
    +
    
    89
    +    override var bridgeTransport: TorBridgeTransportConfig
    
    90
    +        get() {
    
    91
    +            return when (getTorSettings()?.bridgesSource) {
    
    92
    +                BridgeSource.BuiltIn -> {
    
    93
    +                    when (getTorSettings()?.bridgesBuiltinType) {
    
    94
    +                        BridgeBuiltinType.Obfs4 -> TorBridgeTransportConfig.BUILTIN_OBFS4
    
    95
    +                        BridgeBuiltinType.MeekAzure -> TorBridgeTransportConfig.BUILTIN_MEEK_AZURE
    
    96
    +                        BridgeBuiltinType.Snowflake -> TorBridgeTransportConfig.BUILTIN_SNOWFLAKE
    
    97
    +                        else -> TorBridgeTransportConfig.USER_PROVIDED
    
    98
    +                    }
    
    99
    +
    
    100
    +                }
    
    101
    +
    
    102
    +                BridgeSource.UserProvided -> TorBridgeTransportConfig.USER_PROVIDED
    
    103
    +                else -> TorBridgeTransportConfig.USER_PROVIDED
    
    104
    +            }
    
    105
    +        }
    
    106
    +        set(value) {
    
    107
    +            getTorSettings()?.let {
    
    108
    +                if (value == TorBridgeTransportConfig.USER_PROVIDED) {
    
    109
    +                    it.bridgesSource = BridgeSource.BuiltIn
    
    110
    +                } else {
    
    111
    +                    val bbt: BridgeBuiltinType = when (value) {
    
    112
    +                        TorBridgeTransportConfig.BUILTIN_OBFS4 -> BridgeBuiltinType.Obfs4
    
    113
    +                        TorBridgeTransportConfig.BUILTIN_MEEK_AZURE -> BridgeBuiltinType.MeekAzure
    
    114
    +                        TorBridgeTransportConfig.BUILTIN_SNOWFLAKE -> BridgeBuiltinType.Snowflake
    
    115
    +                        else -> BridgeBuiltinType.Invalid
    
    116
    +                    }
    
    117
    +                    it.bridgesBuiltinType = bbt
    
    118
    +                }
    
    119
    +                getTorIntegration().setSettings(it, true, true)
    
    120
    +            }
    
    121
    +        }
    
    122
    +
    
    123
    +
    
    124
    +    override var userProvidedBridges: String?
    
    125
    +        get() {
    
    126
    +            return getTorSettings()?.bridgeBridgeStrings?.joinToString("\r\n")
    
    127
    +        }
    
    128
    +        set(value) {
    
    129
    +            getTorSettings()?.let {
    
    130
    +                it.bridgeBridgeStrings = value?.split("\r\n")?.toTypedArray() ?: arrayOf<String>()
    
    131
    +                getTorIntegration().setSettings(it, true, true)
    
    132
    +            }
    
    133
    +        }
    
    134
    +
    
    135
    +    override fun start() {
    
    136
    +        getTorIntegration().registerBootstrapStateChangeListener(this)
    
    137
    +    }
    
    138
    +
    
    139
    +    override fun stop() {
    
    140
    +        getTorIntegration().unregisterBootstrapStateChangeListener(this)
    
    141
    +    }
    
    142
    +
    
    143
    +    // TorEvents
    
    144
    +    override fun onTorConnecting() {
    
    145
    +        synchronized(torListeners) {
    
    146
    +            torListeners.forEach { it.onTorConnecting() }
    
    147
    +        }
    
    148
    +    }
    
    149
    +
    
    150
    +    // TorEvents
    
    151
    +    override fun onTorConnected() {
    
    152
    +        synchronized(torListeners) {
    
    153
    +            torListeners.forEach { it.onTorConnected() }
    
    154
    +        }
    
    155
    +    }
    
    156
    +
    
    157
    +    // TorEvents
    
    158
    +    override fun onTorStatusUpdate(entry: String?, status: String?) {
    
    159
    +        synchronized(torListeners) {
    
    160
    +            torListeners.forEach { it.onTorStatusUpdate(entry, status) }
    
    161
    +        }
    
    162
    +    }
    
    163
    +
    
    164
    +    // TorEvents
    
    165
    +    override fun onTorStopped() {
    
    166
    +        synchronized(torListeners) {
    
    167
    +            torListeners.forEach { it.onTorStopped() }
    
    168
    +        }
    
    169
    +    }
    
    170
    +
    
    171
    +    override fun registerTorListener(l: TorEvents) {
    
    172
    +        synchronized(torListeners) {
    
    173
    +            if (torListeners.contains(l)) {
    
    174
    +                return
    
    175
    +            }
    
    176
    +            torListeners.add(l)
    
    177
    +        }
    
    178
    +    }
    
    179
    +
    
    180
    +    override fun unregisterTorListener(l: TorEvents) {
    
    181
    +        synchronized(torListeners) {
    
    182
    +            if (!torListeners.contains(l)) {
    
    183
    +                return
    
    184
    +            }
    
    185
    +            torListeners.remove(l)
    
    186
    +        }
    
    187
    +    }
    
    188
    +
    
    189
    +    override fun initiateTorBootstrap(
    
    190
    +        lifecycleScope: LifecycleCoroutineScope?,
    
    191
    +        withDebugLogging: Boolean,
    
    192
    +    ) {
    
    193
    +        getTorIntegration().beginBootstrap()
    
    194
    +    }
    
    195
    +
    
    196
    +    override fun stopTor() {
    
    197
    +        getTorIntegration().cancelBootstrap()
    
    198
    +    }
    
    199
    +
    
    200
    +    override fun setTorStopped() {
    
    201
    +        lastKnownStatus = TorConnectState.Disabled
    
    202
    +        onTorStopped()
    
    203
    +    }
    
    204
    +
    
    205
    +    override fun restartTor() {
    
    206
    +        if (!lastKnownStatus.isStarted() && wasTorBootstrapped) {
    
    207
    +            // If we aren't started, but we were previously bootstrapped,
    
    208
    +            // then we handle a "restart" request as a "start" restart
    
    209
    +            initiateTorBootstrap()
    
    210
    +        } else {
    
    211
    +            // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state
    
    212
    +            // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor
    
    213
    +            // service.
    
    214
    +            isTorRestarting = true
    
    215
    +            stopTor()
    
    216
    +        }
    
    217
    +    }
    
    218
    +
    
    219
    +    // TorEventsBootstrapStateChangeListener -> (lastKnowStatus, TorEvents)
    
    220
    +    // Handle events from GeckoView TorAndroidIntegration and map to TorEvents based events
    
    221
    +    // and state for firefox-android (designed for tor-android-service)
    
    222
    +    //   fun onTorConnecting()
    
    223
    +    //   fun onTorConnected()
    
    224
    +    //   fun onTorStatusUpdate(entry: String?, status: String?)
    
    225
    +    //   fun onTorStopped()
    
    226
    +
    
    227
    +    // TorEventsBootstrapStateChangeListener
    
    228
    +    override fun onBootstrapStateChange(newStateVal: String?) {
    
    229
    +        Log.d(TAG, "onBootstrapStateChange($newStateVal)")
    
    230
    +        val newState: TorConnectState = TorConnectState.valueOf(newStateVal ?: "Error")
    
    231
    +
    
    232
    +        if (newState.isError() && wasTorBootstrapped) {
    
    233
    +            stopTor()
    
    234
    +        }
    
    235
    +
    
    236
    +        if (newState.isStarted()) {
    
    237
    +            wasTorBootstrapped = true
    
    238
    +            onTorConnected()
    
    239
    +        }
    
    240
    +
    
    241
    +        if (wasTorBootstrapped && newState == TorConnectState.Configuring) {
    
    242
    +            wasTorBootstrapped = false
    
    243
    +            if (isTorRestarting) {
    
    244
    +                initiateTorBootstrap()
    
    245
    +            } else {
    
    246
    +                onTorStopped()
    
    247
    +            }
    
    248
    +        }
    
    249
    +
    
    250
    +        if (lastKnownStatus.isOff() && newState.isStarting()) {
    
    251
    +            isTorRestarting = false
    
    252
    +        }
    
    253
    +
    
    254
    +        lastKnownStatus = newState
    
    255
    +
    
    256
    +    }
    
    257
    +
    
    258
    +    // TorEventsBootstrapStateChangeListener
    
    259
    +    override fun onBootstrapProgress(progress: Double, status: String?, hasWarnings: Boolean) {
    
    260
    +        Log.d(TAG, "onBootstrapProgress($progress, $status, $hasWarnings)")
    
    261
    +        if (progress == 100.0) {
    
    262
    +            lastKnownStatus = TorConnectState.Bootstrapped
    
    263
    +            wasTorBootstrapped = true
    
    264
    +            onTorConnected()
    
    265
    +        } else {
    
    266
    +            lastKnownStatus = TorConnectState.Bootstrapping
    
    267
    +            onTorConnecting()
    
    268
    +
    
    269
    +        }
    
    270
    +        entries.add(Pair(status, lastKnownStatus.toTorStatus().status))
    
    271
    +        onTorStatusUpdate(status, lastKnownStatus.toTorStatus().status)
    
    272
    +    }
    
    273
    +
    
    274
    +    // TorEventsBootstrapStateChangeListener
    
    275
    +    override fun onBootstrapComplete() {
    
    276
    +        lastKnownStatus = TorConnectState.Bootstrapped
    
    277
    +        this.onTorConnected()
    
    278
    +    }
    
    279
    +
    
    280
    +    // TorEventsBootstrapStateChangeListener
    
    281
    +    override fun onBootstrapError(message: String?, details: String?) {
    
    282
    +        lastKnownStatus = TorConnectState.Error
    
    283
    +    }
    
    284
    +
    
    285
    +    // TorEventsBootstrapStateChangeListener
    
    286
    +    override fun onSettingsRequested() {
    
    287
    +        // noop
    
    288
    +    }
    
    289
    +}

  • fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerTAS.kt
    1
    +package org.mozilla.fenix.tor
    
    2
    +
    
    3
    +import android.content.BroadcastReceiver
    
    4
    +import android.content.Context
    
    5
    +import android.content.Intent
    
    6
    +import android.content.IntentFilter
    
    7
    +import androidx.lifecycle.LifecycleCoroutineScope
    
    8
    +import androidx.localbroadcastmanager.content.LocalBroadcastManager
    
    9
    +import kotlinx.coroutines.channels.Channel
    
    10
    +import kotlinx.coroutines.launch
    
    11
    +import kotlinx.coroutines.withTimeoutOrNull
    
    12
    +import org.mozilla.fenix.BuildConfig
    
    13
    +import org.torproject.android.service.TorService
    
    14
    +import org.torproject.android.service.TorServiceConstants
    
    15
    +import org.torproject.android.service.util.Prefs
    
    16
    +
    
    17
    +@SuppressWarnings("TooManyFunctions")
    
    18
    +class TorControllerTAS (private val context: Context): TorController {
    
    19
    +    private val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
    
    20
    +    private val entries = mutableListOf<Pair<String?, String?>>()
    
    21
    +    override val logEntries get() = entries
    
    22
    +
    
    23
    +    private var torListeners = mutableListOf<TorEvents>()
    
    24
    +
    
    25
    +    private var pendingRegisterChangeList = mutableListOf<Pair<TorEvents, Boolean>>()
    
    26
    +    private var lockTorListenersMutation = false
    
    27
    +
    
    28
    +    private var lastKnownStatus = TorStatus.OFF
    
    29
    +    private var wasTorBootstrapped = false
    
    30
    +    private var isTorRestarting = false
    
    31
    +
    
    32
    +    // This may be a lie
    
    33
    +    private var isTorBootstrapped = false
    
    34
    +        get() = ((lastKnownStatus == TorStatus.ON) && wasTorBootstrapped)
    
    35
    +
    
    36
    +    val isDebugLoggingEnabled get() =
    
    37
    +        context
    
    38
    +            .getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
    
    39
    +            .getBoolean("pref_enable_logging", false)
    
    40
    +
    
    41
    +    override val isStarting get() = lastKnownStatus.isStarting()
    
    42
    +    override val isRestarting get() = isTorRestarting
    
    43
    +    override val isBootstrapped get() = isTorBootstrapped
    
    44
    +    override val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting)
    
    45
    +
    
    46
    +    override var bridgesEnabled: Boolean
    
    47
    +        get() = Prefs.bridgesEnabled()
    
    48
    +        set(value) { Prefs.putBridgesEnabled(value) }
    
    49
    +
    
    50
    +    override var bridgeTransport: TorBridgeTransportConfig
    
    51
    +        get() {
    
    52
    +            return TorBridgeTransportConfigUtil.getStringToBridgeTransport(
    
    53
    +                Prefs.getBridgesList()
    
    54
    +            )
    
    55
    +        }
    
    56
    +        set(value) {
    
    57
    +            if (value == TorBridgeTransportConfig.USER_PROVIDED) {
    
    58
    +                // Don't set the pref when the value is USER_PROVIDED because
    
    59
    +                // "user_provided" is not a valid bridge or transport type.
    
    60
    +                // This call should be followed by setting userProvidedBridges.
    
    61
    +                return
    
    62
    +            }
    
    63
    +            Prefs.setBridgesList(value.transportName)
    
    64
    +        }
    
    65
    +
    
    66
    +    override var userProvidedBridges: String?
    
    67
    +        get() {
    
    68
    +            val bridges = Prefs.getBridgesList()
    
    69
    +            val bridgeType =
    
    70
    +                TorBridgeTransportConfigUtil.getStringToBridgeTransport(bridges)
    
    71
    +            return when (bridgeType) {
    
    72
    +                TorBridgeTransportConfig.USER_PROVIDED -> bridges
    
    73
    +                else -> null
    
    74
    +            }
    
    75
    +        }
    
    76
    +        set(value) {
    
    77
    +            Prefs.setBridgesList(value)
    
    78
    +        }
    
    79
    +
    
    80
    +    override fun start() {
    
    81
    +        // Register receiver
    
    82
    +        lbm.registerReceiver(
    
    83
    +            persistentBroadcastReceiver,
    
    84
    +            IntentFilter(TorServiceConstants.ACTION_STATUS)
    
    85
    +        )
    
    86
    +        lbm.registerReceiver(
    
    87
    +            persistentBroadcastReceiver,
    
    88
    +            IntentFilter(TorServiceConstants.LOCAL_ACTION_LOG)
    
    89
    +        )
    
    90
    +    }
    
    91
    +
    
    92
    +    override fun stop() {
    
    93
    +        lbm.unregisterReceiver(persistentBroadcastReceiver)
    
    94
    +    }
    
    95
    +
    
    96
    +    private val persistentBroadcastReceiver = object : BroadcastReceiver() {
    
    97
    +        override fun onReceive(context: Context, intent: Intent) {
    
    98
    +            if (intent.action == null ||
    
    99
    +                (intent.action != TorServiceConstants.ACTION_STATUS &&
    
    100
    +                        intent.action != TorServiceConstants.LOCAL_ACTION_LOG)
    
    101
    +            ) {
    
    102
    +                return
    
    103
    +            }
    
    104
    +            val action = intent.action
    
    105
    +
    
    106
    +            val logentry: String?
    
    107
    +            val status: String?
    
    108
    +            if (action == TorServiceConstants.LOCAL_ACTION_LOG) {
    
    109
    +                logentry = intent.getExtras()
    
    110
    +                    ?.getCharSequence(TorServiceConstants.LOCAL_EXTRA_LOG) as? String?
    
    111
    +            } else {
    
    112
    +                logentry = null
    
    113
    +            }
    
    114
    +
    
    115
    +            status = intent.getExtras()
    
    116
    +                ?.getCharSequence(TorServiceConstants.EXTRA_STATUS) as? String?
    
    117
    +
    
    118
    +            if (logentry == null && status == null) {
    
    119
    +                return
    
    120
    +            }
    
    121
    +
    
    122
    +            onTorStatusUpdate(logentry, status)
    
    123
    +
    
    124
    +            if (status == null) {
    
    125
    +                return
    
    126
    +            }
    
    127
    +
    
    128
    +            val newStatus = TorStatus.fromString(status)
    
    129
    +
    
    130
    +            if (newStatus.isUnknown() && wasTorBootstrapped) {
    
    131
    +                stopTor()
    
    132
    +            }
    
    133
    +
    
    134
    +            entries.add(Pair(logentry, status))
    
    135
    +
    
    136
    +            if (logentry != null && logentry.contains(TorServiceConstants.TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)) {
    
    137
    +                wasTorBootstrapped = true
    
    138
    +                onTorConnected()
    
    139
    +            }
    
    140
    +
    
    141
    +            if (lastKnownStatus.isStopping() && newStatus.isOff()) {
    
    142
    +                if (isTorRestarting) {
    
    143
    +                    initiateTorBootstrap()
    
    144
    +                } else {
    
    145
    +                    onTorStopped()
    
    146
    +                }
    
    147
    +            }
    
    148
    +
    
    149
    +            if (lastKnownStatus.isOff() && newStatus.isStarting()) {
    
    150
    +                isTorRestarting = false
    
    151
    +            }
    
    152
    +
    
    153
    +            lastKnownStatus = newStatus
    
    154
    +        }
    
    155
    +    }
    
    156
    +
    
    157
    +    override fun onTorConnecting() {
    
    158
    +        lockTorListenersMutation = true
    
    159
    +        torListeners.forEach { it.onTorConnecting() }
    
    160
    +        lockTorListenersMutation = false
    
    161
    +
    
    162
    +        handlePendingRegistrationChanges()
    
    163
    +    }
    
    164
    +
    
    165
    +    override fun onTorConnected() {
    
    166
    +        lockTorListenersMutation = true
    
    167
    +        torListeners.forEach { it.onTorConnected() }
    
    168
    +        lockTorListenersMutation = false
    
    169
    +
    
    170
    +        handlePendingRegistrationChanges()
    
    171
    +    }
    
    172
    +
    
    173
    +    override fun onTorStatusUpdate(entry: String?, status: String?) {
    
    174
    +        lockTorListenersMutation = true
    
    175
    +        torListeners.forEach { it.onTorStatusUpdate(entry, status) }
    
    176
    +        lockTorListenersMutation = false
    
    177
    +
    
    178
    +        handlePendingRegistrationChanges()
    
    179
    +    }
    
    180
    +
    
    181
    +    override fun onTorStopped() {
    
    182
    +        lockTorListenersMutation = true
    
    183
    +        torListeners.forEach { it.onTorStopped() }
    
    184
    +        lockTorListenersMutation = false
    
    185
    +
    
    186
    +        handlePendingRegistrationChanges()
    
    187
    +    }
    
    188
    +
    
    189
    +    override fun registerTorListener(l: TorEvents) {
    
    190
    +        if (torListeners.contains(l)) {
    
    191
    +            return
    
    192
    +        }
    
    193
    +
    
    194
    +        if (lockTorListenersMutation) {
    
    195
    +            pendingRegisterChangeList.add(Pair(l, true))
    
    196
    +        } else {
    
    197
    +            torListeners.add(l)
    
    198
    +        }
    
    199
    +    }
    
    200
    +
    
    201
    +    override fun unregisterTorListener(l: TorEvents) {
    
    202
    +        if (!torListeners.contains(l)) {
    
    203
    +            return
    
    204
    +        }
    
    205
    +
    
    206
    +        if (lockTorListenersMutation) {
    
    207
    +            pendingRegisterChangeList.add(Pair(l, false))
    
    208
    +        } else {
    
    209
    +            torListeners.remove(l)
    
    210
    +        }
    
    211
    +    }
    
    212
    +
    
    213
    +    private fun handlePendingRegistrationChanges() {
    
    214
    +        pendingRegisterChangeList.forEach {
    
    215
    +            if (it.second) {
    
    216
    +                registerTorListener(it.first)
    
    217
    +            } else {
    
    218
    +                unregisterTorListener(it.first)
    
    219
    +            }
    
    220
    +        }
    
    221
    +
    
    222
    +        pendingRegisterChangeList.clear()
    
    223
    +    }
    
    224
    +
    
    225
    +    /**
    
    226
    +     * Receive the current Tor status.
    
    227
    +     *
    
    228
    +     * Send a request for the current status and receive the response.
    
    229
    +     * Returns true if Tor is running, false otherwise.
    
    230
    +     *
    
    231
    +     */
    
    232
    +    private suspend fun checkTorIsStarted(): Boolean {
    
    233
    +        val channel = Channel<Boolean>()
    
    234
    +
    
    235
    +        // Register receiver
    
    236
    +        val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
    
    237
    +        val localBroadcastReceiver = object : BroadcastReceiver() {
    
    238
    +            override fun onReceive(context: Context, intent: Intent) {
    
    239
    +                val action = intent.action ?: return
    
    240
    +                // We only want ACTION_STATUS messages
    
    241
    +                if (action != TorServiceConstants.ACTION_STATUS) {
    
    242
    +                    return
    
    243
    +                }
    
    244
    +                // The current status has the EXTRA_STATUS key
    
    245
    +                val currentStatus =
    
    246
    +                    intent.getStringExtra(TorServiceConstants.EXTRA_STATUS)
    
    247
    +                channel.trySend(currentStatus === TorServiceConstants.STATUS_ON)
    
    248
    +            }
    
    249
    +        }
    
    250
    +        lbm.registerReceiver(
    
    251
    +            localBroadcastReceiver,
    
    252
    +            IntentFilter(TorServiceConstants.ACTION_STATUS)
    
    253
    +        )
    
    254
    +
    
    255
    +        // Request service status
    
    256
    +        sendServiceAction(TorServiceConstants.ACTION_STATUS)
    
    257
    +
    
    258
    +        // Wait for response and unregister receiver
    
    259
    +        var torIsStarted = false
    
    260
    +        withTimeoutOrNull(torServiceResponseTimeout) {
    
    261
    +            torIsStarted = channel.receive()
    
    262
    +        }
    
    263
    +        lbm.unregisterReceiver(localBroadcastReceiver)
    
    264
    +        return torIsStarted
    
    265
    +    }
    
    266
    +
    
    267
    +    override fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope?, withDebugLogging: Boolean) {
    
    268
    +        if (BuildConfig.DISABLE_TOR) {
    
    269
    +            return
    
    270
    +        }
    
    271
    +
    
    272
    +        context.getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
    
    273
    +            .edit().putBoolean("pref_enable_logging", withDebugLogging).apply()
    
    274
    +
    
    275
    +        if (lifecycleScope == null) {
    
    276
    +            sendServiceAction(TorServiceConstants.ACTION_START)
    
    277
    +        } else {
    
    278
    +            lifecycleScope.launch {
    
    279
    +                val torNeedsStart = !checkTorIsStarted()
    
    280
    +                if (torNeedsStart) {
    
    281
    +                    sendServiceAction(TorServiceConstants.ACTION_START)
    
    282
    +                }
    
    283
    +            }
    
    284
    +        }
    
    285
    +    }
    
    286
    +
    
    287
    +    override fun stopTor() {
    
    288
    +        if (BuildConfig.DISABLE_TOR) {
    
    289
    +            return
    
    290
    +        }
    
    291
    +
    
    292
    +        val torService = Intent(context, TorService::class.java)
    
    293
    +        context.stopService(torService)
    
    294
    +    }
    
    295
    +
    
    296
    +    override fun setTorStopped() {
    
    297
    +        lastKnownStatus = TorStatus.OFF
    
    298
    +        onTorStopped()
    
    299
    +    }
    
    300
    +
    
    301
    +    override fun restartTor() {
    
    302
    +        // tor-android-service doesn't dynamically update the torrc file,
    
    303
    +        // and it doesn't use SETCONF, so we completely restart the service.
    
    304
    +        // However, don't restart if we aren't started and we weren't
    
    305
    +        // previously started.
    
    306
    +        if (!lastKnownStatus.isStarted() && !wasTorBootstrapped) {
    
    307
    +            return
    
    308
    +        }
    
    309
    +
    
    310
    +        if (!lastKnownStatus.isStarted() && wasTorBootstrapped) {
    
    311
    +            // If we aren't started, but we were previously bootstrapped,
    
    312
    +            // then we handle a "restart" request as a "start" restart
    
    313
    +            initiateTorBootstrap()
    
    314
    +        } else {
    
    315
    +            // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state
    
    316
    +            // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor
    
    317
    +            // service.
    
    318
    +            isTorRestarting = true
    
    319
    +            stopTor()
    
    320
    +        }
    
    321
    +    }
    
    322
    +
    
    323
    +    private fun sendServiceAction(action: String) {
    
    324
    +        val torServiceStatus = Intent(context, TorService::class.java)
    
    325
    +        torServiceStatus.action = action
    
    326
    +        context.startService(torServiceStatus)
    
    327
    +    }
    
    328
    +
    
    329
    +    companion object {
    
    330
    +        const val torServiceResponseTimeout = 5000L
    
    331
    +    }
    
    332
    +}