Class that uses the androidx.webkit
library to override device WebView
's proxy settings
such that application with Tor bundled into it can connect to hidden services for serving html.
/*
* Copyright 2021 Matthew Nelson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* */
import android.annotation.SuppressLint
import androidx.webkit.ProxyConfig
import androidx.webkit.ProxyController
import androidx.webkit.WebViewFeature
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.util.concurrent.Executor
object WebViewProxyOverride {
@Suppress("RemoveExplicitTypeArguments", "ObjectPropertyName")
private val _proxyOverrideStateFlow: MutableStateFlow<ProxyOverrideState> by lazy {
MutableStateFlow<ProxyOverrideState>(ProxyOverrideState.Uninitialized)
}
val proxyOverrideStateFlow: StateFlow<ProxyOverrideState>
get() = _proxyOverrideStateFlow.asStateFlow()
private class SynchronousExecutor: Executor {
override fun execute(command: Runnable?) {
command?.run()
}
}
/**
* Helper method for discerning if the functionality for overriding the WebView's
* system proxy is supported for the device.
*
* @see [WebViewFeature.isFeatureSupported]
* */
fun isWebViewProxyOverrideSupported(): Boolean =
WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)
@SuppressLint("RequiresFeature")
fun setSocksProxy(
socksHost: String,
socksPort: Int,
proxyConfigBuilder: ProxyConfig.Builder = ProxyConfig.Builder()
) {
proxyConfigBuilder
.addProxyRule("socks://${socksHost}:${socksPort}")
.build().let { proxyConfig ->
try {
ProxyController.getInstance().setProxyOverride(
proxyConfig,
SynchronousExecutor(),
{
_proxyOverrideStateFlow.value = ProxyOverrideState.ProxySet
}
)
} catch (e: UnsupportedOperationException) {
_proxyOverrideStateFlow.value = ProxyOverrideState.Error.OverrideUnsupported(e)
} catch (e: IllegalArgumentException) {
_proxyOverrideStateFlow.value = ProxyOverrideState.Error.InvalidConfig(e)
}
}
}
@SuppressLint("RequiresFeature")
fun clearProxy() {
if (
_proxyOverrideStateFlow.value == ProxyOverrideState.ProxyCleared ||
_proxyOverrideStateFlow.value == ProxyOverrideState.Uninitialized
) {
return
}
try {
ProxyController.getInstance().clearProxyOverride(
SynchronousExecutor(),
{
_proxyOverrideStateFlow.value = ProxyOverrideState.ProxyCleared
}
)
} catch (e: UnsupportedOperationException) {
_proxyOverrideStateFlow.value = ProxyOverrideState.Error.OverrideUnsupported(e)
}
}
}
I've tried:
.addProxyRule("socks://127.0.0.1:9050")
but the WebView still doesn't connect to Tor network.
and I've started kmp-tor lib ofcourse:
Do I miss anything?