parent
f6175c2c69
commit
3e02dfef63
22 changed files with 577 additions and 154 deletions
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Leave package attribute for androiddeployqt -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.amnezia.vpn"
|
||||
android:versionName="-- %%INSERT_VERSION_NAME%% --"
|
||||
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
||||
|
@ -31,7 +33,10 @@
|
|||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:icon="@mipmap/icon"
|
||||
android:roundIcon="@mipmap/icon_round"
|
||||
android:theme="@style/NoActionBar">
|
||||
android:theme="@style/NoActionBar"
|
||||
android:fullBackupContent="@xml/backup_content"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
tools:targetApi="s">
|
||||
|
||||
<activity
|
||||
android:name=".AmneziaActivity"
|
||||
|
@ -147,7 +152,7 @@
|
|||
android:authorities="org.amnezia.vpn.qtprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -108,7 +108,6 @@ dependencies {
|
|||
implementation(project(":cloak"))
|
||||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.activity)
|
||||
implementation(libs.androidx.security.crypto)
|
||||
implementation(libs.kotlinx.coroutines)
|
||||
implementation(libs.bundles.androidx.camera)
|
||||
implementation(libs.google.mlkit)
|
||||
|
|
|
@ -52,7 +52,7 @@ class OpenVpnClient(
|
|||
// Callback to construct a new tun builder
|
||||
// Should be called first.
|
||||
override fun tun_builder_new(): Boolean {
|
||||
Log.v(TAG, "tun_builder_new")
|
||||
Log.d(TAG, "tun_builder_new")
|
||||
configBuilder.clearAddresses()
|
||||
return true
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class OpenVpnClient(
|
|||
// Callback to set MTU of the VPN interface
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_mtu(mtu: Int): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_mtu: $mtu")
|
||||
Log.d(TAG, "tun_builder_set_mtu: $mtu")
|
||||
configBuilder.setMtu(mtu)
|
||||
return true
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ class OpenVpnClient(
|
|||
address: String, prefix_length: Int,
|
||||
gateway: String, ipv6: Boolean, net30: Boolean
|
||||
): Boolean {
|
||||
Log.v(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30")
|
||||
Log.d(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30")
|
||||
configBuilder.addAddress(InetNetwork(address, prefix_length))
|
||||
return true
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ class OpenVpnClient(
|
|||
// May be called more than once per tun_builder session
|
||||
// metric is optional and should be ignored if < 0
|
||||
override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
|
||||
Log.v(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6")
|
||||
Log.d(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6")
|
||||
if (address == "remote_host") return false
|
||||
configBuilder.addRoute(InetNetwork(address, prefix_length))
|
||||
return true
|
||||
|
@ -90,7 +90,7 @@ class OpenVpnClient(
|
|||
// May be called more than once per tun_builder session
|
||||
// metric is optional and should be ignored if < 0
|
||||
override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
|
||||
Log.v(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
|
||||
Log.d(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
configBuilder.excludeRoute(InetNetwork(address, prefix_length))
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ class OpenVpnClient(
|
|||
// domain should be routed.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
override fun tun_builder_add_dns_server(address: String, ipv6: Boolean): Boolean {
|
||||
Log.v(TAG, "tun_builder_add_dns_server: $address, $ipv6")
|
||||
Log.d(TAG, "tun_builder_add_dns_server: $address, $ipv6")
|
||||
configBuilder.addDnsServer(parseInetAddress(address))
|
||||
return true
|
||||
}
|
||||
|
@ -119,28 +119,28 @@ class OpenVpnClient(
|
|||
// ignored for that family
|
||||
// See also Android's VPNService.Builder.allowFamily method
|
||||
/* override fun tun_builder_set_allow_family(af: Int, allow: Boolean): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_allow_family: $af, $allow")
|
||||
Log.d(TAG, "tun_builder_set_allow_family: $af, $allow")
|
||||
return true
|
||||
} */
|
||||
|
||||
// Callback to set address of remote server
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_remote_address(address: String, ipv6: Boolean): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_remote_address: $address, $ipv6")
|
||||
Log.d(TAG, "tun_builder_set_remote_address: $address, $ipv6")
|
||||
return true
|
||||
}
|
||||
|
||||
// Optional callback that indicates OSI layer, should be 2 or 3.
|
||||
// Defaults to 3.
|
||||
override fun tun_builder_set_layer(layer: Int): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_layer: $layer")
|
||||
Log.d(TAG, "tun_builder_set_layer: $layer")
|
||||
return layer == 3
|
||||
}
|
||||
|
||||
// Callback to set the session name
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_session_name(name: String): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_session_name: $name")
|
||||
Log.d(TAG, "tun_builder_set_session_name: $name")
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ class OpenVpnClient(
|
|||
// if the tunnel could not be established.
|
||||
// Always called last after tun_builder session has been configured.
|
||||
override fun tun_builder_establish(): Int {
|
||||
Log.v(TAG, "tun_builder_establish")
|
||||
Log.d(TAG, "tun_builder_establish")
|
||||
return establish(configBuilder)
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ class OpenVpnClient(
|
|||
// flags are defined in RGWFlags (rgwflags.hpp).
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_reroute_gw(ipv4: Boolean, ipv6: Boolean, flags: Long): Boolean {
|
||||
Log.v(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags")
|
||||
Log.d(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags")
|
||||
if ((flags and EMULATED_EXCLUDE_ROUTES.toLong()) != 0L) return true
|
||||
if (ipv4) {
|
||||
configBuilder.addRoute(InetNetwork("0.0.0.0", 0))
|
||||
|
@ -176,7 +176,7 @@ class OpenVpnClient(
|
|||
// reroute_dns parameter.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
override fun tun_builder_add_search_domain(domain: String): Boolean {
|
||||
Log.v(TAG, "tun_builder_add_search_domain: $domain")
|
||||
Log.d(TAG, "tun_builder_add_search_domain: $domain")
|
||||
configBuilder.setSearchDomain(domain)
|
||||
return true
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ class OpenVpnClient(
|
|||
// Callback to set the HTTP proxy
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_proxy_http: $host, $port")
|
||||
Log.d(TAG, "tun_builder_set_proxy_http: $host, $port")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
try {
|
||||
configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port))
|
||||
|
@ -199,7 +199,7 @@ class OpenVpnClient(
|
|||
// Callback to set the HTTPS proxy
|
||||
// Never called more than once per tun_builder session.
|
||||
override fun tun_builder_set_proxy_https(host: String, port: Int): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_proxy_https: $host, $port")
|
||||
Log.d(TAG, "tun_builder_set_proxy_https: $host, $port")
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ class OpenVpnClient(
|
|||
// to exclude them from the VPN network are generated
|
||||
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
|
||||
override fun tun_builder_get_local_networks(ipv6: Boolean): ClientAPI_StringVec {
|
||||
Log.v(TAG, "tun_builder_get_local_networks: $ipv6")
|
||||
Log.d(TAG, "tun_builder_get_local_networks: $ipv6")
|
||||
val networks = ClientAPI_StringVec()
|
||||
for (address in getLocalNetworks(ipv6)) {
|
||||
networks.add(address.toString())
|
||||
|
@ -222,21 +222,21 @@ class OpenVpnClient(
|
|||
// tun_builder_reroute_gw. Route metric is ignored
|
||||
// if < 0.
|
||||
/* override fun tun_builder_set_route_metric_default(metric: Int): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_route_metric_default: $metric")
|
||||
Log.d(TAG, "tun_builder_set_route_metric_default: $metric")
|
||||
return super.tun_builder_set_route_metric_default(metric)
|
||||
} */
|
||||
|
||||
// Callback to add a host which should bypass the proxy
|
||||
// May be called more than once per tun_builder session
|
||||
/* override fun tun_builder_add_proxy_bypass(bypass_host: String): Boolean {
|
||||
Log.v(TAG, "tun_builder_add_proxy_bypass: $bypass_host")
|
||||
Log.d(TAG, "tun_builder_add_proxy_bypass: $bypass_host")
|
||||
return super.tun_builder_add_proxy_bypass(bypass_host)
|
||||
} */
|
||||
|
||||
// Callback to set the proxy "Auto Config URL"
|
||||
// Never called more than once per tun_builder session.
|
||||
/* override fun tun_builder_set_proxy_auto_config_url(url: String): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_proxy_auto_config_url: $url")
|
||||
Log.d(TAG, "tun_builder_set_proxy_auto_config_url: $url")
|
||||
return super.tun_builder_set_proxy_auto_config_url(url)
|
||||
} */
|
||||
|
||||
|
@ -245,7 +245,7 @@ class OpenVpnClient(
|
|||
// May be called more than once per tun_builder session.
|
||||
// Guaranteed to be called after tun_builder_reroute_gw.
|
||||
/* override fun tun_builder_add_wins_server(address: String): Boolean {
|
||||
Log.v(TAG, "tun_builder_add_wins_server: $address")
|
||||
Log.d(TAG, "tun_builder_add_wins_server: $address")
|
||||
return super.tun_builder_add_wins_server(address)
|
||||
} */
|
||||
|
||||
|
@ -254,7 +254,7 @@ class OpenVpnClient(
|
|||
// set the "Connection-specific DNS Suffix" property on
|
||||
// the TAP driver.
|
||||
/* override fun tun_builder_set_adapter_domain_suffix(name: String): Boolean {
|
||||
Log.v(TAG, "tun_builder_set_adapter_domain_suffix: $name")
|
||||
Log.d(TAG, "tun_builder_set_adapter_domain_suffix: $name")
|
||||
return super.tun_builder_set_adapter_domain_suffix(name)
|
||||
} */
|
||||
|
||||
|
@ -266,13 +266,13 @@ class OpenVpnClient(
|
|||
// tun_builder_establish_lite() will be called. Otherwise,
|
||||
// tun_builder_establish() will be called.
|
||||
/* override fun tun_builder_persist(): Boolean {
|
||||
Log.v(TAG, "tun_builder_persist")
|
||||
Log.d(TAG, "tun_builder_persist")
|
||||
return super.tun_builder_persist()
|
||||
} */
|
||||
|
||||
// Indicates a reconnection with persisted tun state.
|
||||
/* override fun tun_builder_establish_lite() {
|
||||
Log.v(TAG, "tun_builder_establish_lite")
|
||||
Log.d(TAG, "tun_builder_establish_lite")
|
||||
super.tun_builder_establish_lite()
|
||||
} */
|
||||
|
||||
|
@ -280,7 +280,7 @@ class OpenVpnClient(
|
|||
// If disconnect == true, then the teardown is occurring
|
||||
// prior to final disconnect.
|
||||
/* override fun tun_builder_teardown(disconnect: Boolean) {
|
||||
Log.v(TAG, "tun_builder_teardown: $disconnect")
|
||||
Log.d(TAG, "tun_builder_teardown: $disconnect")
|
||||
super.tun_builder_teardown(disconnect)
|
||||
} */
|
||||
|
||||
|
@ -290,7 +290,7 @@ class OpenVpnClient(
|
|||
|
||||
// Parse OpenVPN configuration file.
|
||||
override fun eval_config(arg0: ClientAPI_Config): ClientAPI_EvalConfig {
|
||||
Log.v(TAG, "eval_config")
|
||||
Log.d(TAG, "eval_config")
|
||||
return super.eval_config(arg0)
|
||||
}
|
||||
|
||||
|
@ -299,7 +299,7 @@ class OpenVpnClient(
|
|||
// to event() and log() functions. Make sure to call eval_config()
|
||||
// and possibly provide_creds() as well before this function.
|
||||
override fun connect(): ClientAPI_Status {
|
||||
Log.v(TAG, "connect")
|
||||
Log.d(TAG, "connect")
|
||||
return super.connect()
|
||||
}
|
||||
|
||||
|
@ -307,7 +307,7 @@ class OpenVpnClient(
|
|||
// Will be called from the thread executing connect().
|
||||
// The remote and ipv6 are the remote host this socket will connect to
|
||||
override fun socket_protect(socket: Int, remote: String, ipv6: Boolean): Boolean {
|
||||
Log.v(TAG, "socket_protect: $socket, $remote, $ipv6")
|
||||
Log.d(TAG, "socket_protect: $socket, $remote, $ipv6")
|
||||
return protect(socket)
|
||||
}
|
||||
|
||||
|
@ -315,7 +315,7 @@ class OpenVpnClient(
|
|||
// May be called asynchronously from a different thread
|
||||
// when connect() is running.
|
||||
override fun stop() {
|
||||
Log.v(TAG, "stop")
|
||||
Log.d(TAG, "stop")
|
||||
super.stop()
|
||||
}
|
||||
|
||||
|
@ -323,21 +323,21 @@ class OpenVpnClient(
|
|||
// when network is down. May be called from a different thread
|
||||
// when connect() is running.
|
||||
override fun pause(reason: String) {
|
||||
Log.v(TAG, "pause: $reason")
|
||||
Log.d(TAG, "pause: $reason")
|
||||
super.pause(reason)
|
||||
}
|
||||
|
||||
// Resume the client after it has been paused. May be called from a
|
||||
// different thread when connect() is running.
|
||||
override fun resume() {
|
||||
Log.v(TAG, "resume")
|
||||
Log.d(TAG, "resume")
|
||||
super.resume()
|
||||
}
|
||||
|
||||
// Do a disconnect/reconnect cycle n seconds from now. May be called
|
||||
// from a different thread when connect() is running.
|
||||
override fun reconnect(seconds: Int) {
|
||||
Log.v(TAG, "reconnect")
|
||||
Log.d(TAG, "reconnect: $seconds")
|
||||
super.reconnect(seconds)
|
||||
}
|
||||
|
||||
|
@ -346,14 +346,14 @@ class OpenVpnClient(
|
|||
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
|
||||
// state.
|
||||
override fun pause_on_connection_timeout(): Boolean {
|
||||
Log.v(TAG, "pause_on_connection_timeout")
|
||||
Log.d(TAG, "pause_on_connection_timeout")
|
||||
return false
|
||||
}
|
||||
|
||||
// Return information about the most recent connection. Should be called
|
||||
// after an event of type "CONNECTED".
|
||||
/* override fun connection_info(): ClientAPI_ConnectionInfo {
|
||||
Log.v(TAG, "connection_info")
|
||||
Log.d(TAG, "connection_info")
|
||||
return super.connection_info()
|
||||
} */
|
||||
|
||||
|
@ -366,7 +366,7 @@ class OpenVpnClient(
|
|||
override fun event(event: ClientAPI_Event) {
|
||||
val name = event.name
|
||||
val info = event.info
|
||||
Log.v(TAG, "OpenVpn event: $name: $info")
|
||||
Log.d(TAG, "OpenVpn event: $name: $info")
|
||||
when (name) {
|
||||
"COMPRESSION_ENABLED", "WARN" -> Log.w(TAG, "$name: $info")
|
||||
"CONNECTED" -> state.value = CONNECTED
|
||||
|
@ -398,31 +398,31 @@ class OpenVpnClient(
|
|||
|
||||
// return transport stats only
|
||||
override fun transport_stats(): ClientAPI_TransportStats {
|
||||
Log.v(TAG, "transport_stats")
|
||||
Log.d(TAG, "transport_stats")
|
||||
return super.transport_stats()
|
||||
}
|
||||
|
||||
// return a stats value, index should be >= 0 and < stats_n()
|
||||
/* override fun stats_value(index: Int): Long {
|
||||
Log.v(TAG, "stats_value: $index")
|
||||
Log.d(TAG, "stats_value: $index")
|
||||
return super.stats_value(index)
|
||||
} */
|
||||
|
||||
// return all stats in a bundle
|
||||
/* override fun stats_bundle(): ClientAPI_LLVector {
|
||||
Log.v(TAG, "stats_bundle")
|
||||
Log.d(TAG, "stats_bundle")
|
||||
return super.stats_bundle()
|
||||
} */
|
||||
|
||||
// return tun stats only
|
||||
/* override fun tun_stats(): ClientAPI_InterfaceStats {
|
||||
Log.v(TAG, "tun_stats")
|
||||
Log.d(TAG, "tun_stats")
|
||||
return super.tun_stats()
|
||||
} */
|
||||
|
||||
// post control channel message
|
||||
/* override fun post_cc_msg(msg: String) {
|
||||
Log.v(TAG, "post_cc_msg: $msg")
|
||||
Log.d(TAG, "post_cc_msg: $msg")
|
||||
super.post_cc_msg(msg)
|
||||
} */
|
||||
}
|
||||
|
|
4
client/android/res/xml/backup_content.xml
Normal file
4
client/android/res/xml/backup_content.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<full-backup-content>
|
||||
<exclude domain="sharedpref" path="." />
|
||||
</full-backup-content>
|
9
client/android/res/xml/data_extraction_rules.xml
Normal file
9
client/android/res/xml/data_extraction_rules.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude domain="sharedpref" path="." />
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
<exclude domain="sharedpref" path="." />
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
|
@ -143,7 +143,7 @@ class AmneziaActivity : QtActivity() {
|
|||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.v(TAG, "Create Amnezia activity: $intent")
|
||||
Log.d(TAG, "Create Amnezia activity: $intent")
|
||||
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||
vpnServiceMessenger = IpcMessenger(
|
||||
onDeadObjectException = ::doUnbindService,
|
||||
|
@ -154,7 +154,7 @@ class AmneziaActivity : QtActivity() {
|
|||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
Log.v(TAG, "onNewIntent: $intent")
|
||||
Log.d(TAG, "onNewIntent: $intent")
|
||||
intent?.let(::processIntent)
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ class AmneziaActivity : QtActivity() {
|
|||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
Log.v(TAG, "Start Amnezia activity")
|
||||
Log.d(TAG, "Start Amnezia activity")
|
||||
mainScope.launch {
|
||||
qtInitialized.await()
|
||||
doBindService()
|
||||
|
@ -182,13 +182,13 @@ class AmneziaActivity : QtActivity() {
|
|||
}
|
||||
|
||||
override fun onStop() {
|
||||
Log.v(TAG, "Stop Amnezia activity")
|
||||
Log.d(TAG, "Stop Amnezia activity")
|
||||
doUnbindService()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.v(TAG, "Destroy Amnezia activity")
|
||||
Log.d(TAG, "Destroy Amnezia activity")
|
||||
mainScope.cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ class AmneziaActivity : QtActivity() {
|
|||
CHECK_VPN_PERMISSION_ACTION_CODE -> {
|
||||
when (resultCode) {
|
||||
RESULT_OK -> {
|
||||
Log.v(TAG, "Vpn permission granted")
|
||||
Log.d(TAG, "Vpn permission granted")
|
||||
Toast.makeText(this, "Vpn permission granted", Toast.LENGTH_LONG).show()
|
||||
checkVpnPermissionCallbacks?.run { onSuccess() }
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ class AmneziaActivity : QtActivity() {
|
|||
*/
|
||||
@MainThread
|
||||
private fun doBindService() {
|
||||
Log.v(TAG, "Bind service")
|
||||
Log.d(TAG, "Bind service")
|
||||
Intent(this, AmneziaVpnService::class.java).also {
|
||||
bindService(it, serviceConnection, BIND_ABOVE_CLIENT)
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ class AmneziaActivity : QtActivity() {
|
|||
@MainThread
|
||||
private fun doUnbindService() {
|
||||
if (isInBoundState) {
|
||||
Log.v(TAG, "Unbind service")
|
||||
Log.d(TAG, "Unbind service")
|
||||
isWaitingStatus = true
|
||||
QtAndroidController.onServiceDisconnected()
|
||||
vpnServiceMessenger.reset()
|
||||
|
@ -286,7 +286,7 @@ class AmneziaActivity : QtActivity() {
|
|||
|
||||
@MainThread
|
||||
private fun checkVpnPermission(onSuccess: () -> Unit, onFail: () -> Unit) {
|
||||
Log.v(TAG, "Check VPN permission")
|
||||
Log.d(TAG, "Check VPN permission")
|
||||
VpnService.prepare(applicationContext)?.let {
|
||||
checkVpnPermissionCallbacks = CheckVpnPermissionCallbacks(onSuccess, onFail)
|
||||
startActivityForResult(it, CHECK_VPN_PERMISSION_ACTION_CODE)
|
||||
|
@ -307,7 +307,7 @@ class AmneziaActivity : QtActivity() {
|
|||
}
|
||||
|
||||
private fun connectToVpn(vpnConfig: String) {
|
||||
Log.v(TAG, "Connect to VPN")
|
||||
Log.d(TAG, "Connect to VPN")
|
||||
vpnServiceMessenger.send {
|
||||
Action.CONNECT.packToMessage {
|
||||
putString(VPN_CONFIG, vpnConfig)
|
||||
|
@ -316,7 +316,7 @@ class AmneziaActivity : QtActivity() {
|
|||
}
|
||||
|
||||
private fun startVpnService(vpnConfig: String) {
|
||||
Log.v(TAG, "Start VPN service")
|
||||
Log.d(TAG, "Start VPN service")
|
||||
Intent(this, AmneziaVpnService::class.java).apply {
|
||||
putExtra(VPN_CONFIG, vpnConfig)
|
||||
}.also {
|
||||
|
@ -325,7 +325,7 @@ class AmneziaActivity : QtActivity() {
|
|||
}
|
||||
|
||||
private fun disconnectFromVpn() {
|
||||
Log.v(TAG, "Disconnect from VPN")
|
||||
Log.d(TAG, "Disconnect from VPN")
|
||||
vpnServiceMessenger.send(Action.DISCONNECT)
|
||||
}
|
||||
|
||||
|
@ -369,7 +369,7 @@ class AmneziaActivity : QtActivity() {
|
|||
|
||||
@Suppress("unused")
|
||||
fun saveFile(fileName: String, data: String) {
|
||||
Log.v(TAG, "Save file $fileName")
|
||||
Log.d(TAG, "Save file $fileName")
|
||||
mainScope.launch {
|
||||
tmpFileContentToSave = data
|
||||
|
||||
|
@ -397,7 +397,7 @@ class AmneziaActivity : QtActivity() {
|
|||
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
Log.d(TAG, "File mimyType filter: $mimeTypes")
|
||||
Log.v(TAG, "File mimyType filter: $mimeTypes")
|
||||
when (mimeTypes.size) {
|
||||
1 -> type = mimeTypes.first()
|
||||
|
||||
|
@ -416,13 +416,6 @@ class AmneziaActivity : QtActivity() {
|
|||
@Suppress("unused")
|
||||
fun setNotificationText(title: String, message: String, timerSec: Int) {
|
||||
Log.v(TAG, "Set notification text")
|
||||
Log.w(TAG, "Not yet implemented")
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun cleanupLogs() {
|
||||
Log.v(TAG, "Cleanup logs")
|
||||
Log.w(TAG, "Not yet implemented")
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
|
@ -432,4 +425,29 @@ class AmneziaActivity : QtActivity() {
|
|||
startActivity(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun setSaveLogs(enabled: Boolean) {
|
||||
Log.d(TAG, "Set save logs: $enabled")
|
||||
mainScope.launch {
|
||||
Log.saveLogs = enabled
|
||||
vpnServiceMessenger.send {
|
||||
Action.SET_SAVE_LOGS.packToMessage {
|
||||
putBoolean(SAVE_LOGS, enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun exportLogsFile(fileName: String) {
|
||||
Log.v(TAG, "Export logs file")
|
||||
saveFile(fileName, Log.getLogs())
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun clearLogs() {
|
||||
Log.v(TAG, "Clear logs")
|
||||
Log.clearLogs()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,20 @@ import androidx.camera.core.CameraSelector
|
|||
import androidx.camera.core.CameraXConfig
|
||||
import androidx.core.app.NotificationChannelCompat.Builder
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import org.amnezia.vpn.util.Log
|
||||
import org.amnezia.vpn.util.Prefs
|
||||
import org.qtproject.qt.android.bindings.QtApplication
|
||||
|
||||
private const val TAG = "AmneziaApplication"
|
||||
const val NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notification"
|
||||
|
||||
class AmneziaApplication : QtApplication(), CameraXConfig.Provider {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Prefs.init(this)
|
||||
Log.init(this)
|
||||
Log.d(TAG, "Create Amnezia application")
|
||||
createNotificationChannel()
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.amnezia.vpn.protocol.putStatistics
|
|||
import org.amnezia.vpn.protocol.putStatus
|
||||
import org.amnezia.vpn.protocol.wireguard.Wireguard
|
||||
import org.amnezia.vpn.util.Log
|
||||
import org.amnezia.vpn.util.Prefs
|
||||
import org.amnezia.vpn.util.net.NetworkState
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
@ -58,6 +59,8 @@ private const val TAG = "AmneziaVpnService"
|
|||
|
||||
const val VPN_CONFIG = "VPN_CONFIG"
|
||||
const val ERROR_MSG = "ERROR_MSG"
|
||||
const val SAVE_LOGS = "SAVE_LOGS"
|
||||
|
||||
const val AFTER_PERMISSION_CHECK = "AFTER_PERMISSION_CHECK"
|
||||
private const val PREFS_CONFIG_KEY = "LAST_CONF"
|
||||
private const val NOTIFICATION_ID = 1337
|
||||
|
@ -118,7 +121,7 @@ class AmneziaVpnService : VpnService() {
|
|||
|
||||
Action.CONNECT -> {
|
||||
val vpnConfig = msg.data.getString(VPN_CONFIG)
|
||||
saveConfigToPrefs(vpnConfig)
|
||||
Prefs.save(PREFS_CONFIG_KEY, vpnConfig)
|
||||
connect(vpnConfig)
|
||||
}
|
||||
|
||||
|
@ -135,6 +138,10 @@ class AmneziaVpnService : VpnService() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Action.SET_SAVE_LOGS -> {
|
||||
Log.saveLogs = msg.data.getBoolean(SAVE_LOGS)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +186,7 @@ class AmneziaVpnService : VpnService() {
|
|||
*/
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.v(TAG, "Create Amnezia VPN service")
|
||||
Log.d(TAG, "Create Amnezia VPN service")
|
||||
mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||
connectionScope = CoroutineScope(SupervisorJob() + Dispatchers.IO + connectionExceptionHandler)
|
||||
clientMessenger = IpcMessenger(messengerName = "Client")
|
||||
|
@ -193,15 +200,15 @@ class AmneziaVpnService : VpnService() {
|
|||
else intent?.component?.packageName != packageName
|
||||
|
||||
if (isAlwaysOnCompat) {
|
||||
Log.v(TAG, "Start service via Always-on")
|
||||
connect(loadConfigFromPrefs())
|
||||
Log.d(TAG, "Start service via Always-on")
|
||||
connect(Prefs.load(PREFS_CONFIG_KEY))
|
||||
} else if (intent?.getBooleanExtra(AFTER_PERMISSION_CHECK, false) == true) {
|
||||
Log.v(TAG, "Start service after permission check")
|
||||
connect(loadConfigFromPrefs())
|
||||
Log.d(TAG, "Start service after permission check")
|
||||
connect(Prefs.load(PREFS_CONFIG_KEY))
|
||||
} else {
|
||||
Log.v(TAG, "Start service")
|
||||
Log.d(TAG, "Start service")
|
||||
val vpnConfig = intent?.getStringExtra(VPN_CONFIG)
|
||||
saveConfigToPrefs(vpnConfig)
|
||||
Prefs.save(PREFS_CONFIG_KEY, vpnConfig)
|
||||
connect(vpnConfig)
|
||||
}
|
||||
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, foregroundServiceTypeCompat)
|
||||
|
@ -237,7 +244,7 @@ class AmneziaVpnService : VpnService() {
|
|||
}
|
||||
|
||||
override fun onRevoke() {
|
||||
Log.v(TAG, "onRevoke")
|
||||
Log.d(TAG, "onRevoke")
|
||||
// Calls to onRevoke() method may not happen on the main thread of the process
|
||||
mainScope.launch {
|
||||
disconnect()
|
||||
|
@ -245,7 +252,7 @@ class AmneziaVpnService : VpnService() {
|
|||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.v(TAG, "Destroy service")
|
||||
Log.d(TAG, "Destroy service")
|
||||
runBlocking {
|
||||
disconnect()
|
||||
disconnectionJob?.join()
|
||||
|
@ -256,7 +263,7 @@ class AmneziaVpnService : VpnService() {
|
|||
}
|
||||
|
||||
private fun stopService() {
|
||||
Log.v(TAG, "Stop service")
|
||||
Log.d(TAG, "Stop service")
|
||||
// the coroutine below will be canceled during the onDestroy call
|
||||
mainScope.launch {
|
||||
delay(STOP_SERVICE_TIMEOUT)
|
||||
|
@ -272,7 +279,7 @@ class AmneziaVpnService : VpnService() {
|
|||
private fun launchProtocolStateHandler() {
|
||||
mainScope.launch {
|
||||
protocolState.collect { protocolState ->
|
||||
Log.d(TAG, "Protocol state: $protocolState")
|
||||
Log.d(TAG, "Protocol state changed: $protocolState")
|
||||
when (protocolState) {
|
||||
CONNECTED -> {
|
||||
clientMessenger.send(ServiceEvent.CONNECTED)
|
||||
|
@ -305,7 +312,7 @@ class AmneziaVpnService : VpnService() {
|
|||
|
||||
@MainThread
|
||||
private fun launchSendingStatistics() {
|
||||
if (isServiceBound && isConnected) {
|
||||
/* if (isServiceBound && isConnected) {
|
||||
statisticsSendingJob = mainScope.launch {
|
||||
while (true) {
|
||||
clientMessenger.send {
|
||||
|
@ -316,7 +323,7 @@ class AmneziaVpnService : VpnService() {
|
|||
delay(STATISTICS_SENDING_TIMEOUT)
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
@MainThread
|
||||
|
@ -328,7 +335,7 @@ class AmneziaVpnService : VpnService() {
|
|||
private fun connect(vpnConfig: String?) {
|
||||
if (isConnected || protocolState.value == CONNECTING) return
|
||||
|
||||
Log.v(TAG, "Start VPN connection")
|
||||
Log.d(TAG, "Start VPN connection")
|
||||
|
||||
protocolState.value = CONNECTING
|
||||
|
||||
|
@ -357,7 +364,7 @@ class AmneziaVpnService : VpnService() {
|
|||
private fun disconnect() {
|
||||
if (isUnknown || isDisconnected || protocolState.value == DISCONNECTING) return
|
||||
|
||||
Log.v(TAG, "Stop VPN connection")
|
||||
Log.d(TAG, "Stop VPN connection")
|
||||
|
||||
protocolState.value = DISCONNECTING
|
||||
|
||||
|
@ -383,7 +390,7 @@ class AmneziaVpnService : VpnService() {
|
|||
private fun reconnect() {
|
||||
if (!isConnected) return
|
||||
|
||||
Log.v(TAG, "Reconnect VPN")
|
||||
Log.d(TAG, "Reconnect VPN")
|
||||
|
||||
protocolState.value = RECONNECTING
|
||||
|
||||
|
@ -439,10 +446,4 @@ class AmneziaVpnService : VpnService() {
|
|||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
private fun loadConfigFromPrefs(): String? =
|
||||
Prefs.get(this).getString(PREFS_CONFIG_KEY, null)
|
||||
|
||||
private fun saveConfigToPrefs(config: String?) =
|
||||
Prefs.get(this).edit().putString(PREFS_CONFIG_KEY, config).apply()
|
||||
}
|
||||
|
|
|
@ -29,20 +29,20 @@ class ImportConfigActivity : ComponentActivity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.v(TAG, "Create Import Config Activity: $intent")
|
||||
Log.d(TAG, "Create Import Config Activity: $intent")
|
||||
intent?.let(::readConfig)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
Log.v(TAG, "onNewIntent: $intent")
|
||||
Log.d(TAG, "onNewIntent: $intent")
|
||||
intent?.let(::readConfig)
|
||||
}
|
||||
|
||||
private fun readConfig(intent: Intent) {
|
||||
when (intent.action) {
|
||||
ACTION_SEND -> {
|
||||
Log.v(TAG, "Process SEND action, type: ${intent.type}")
|
||||
Log.d(TAG, "Process SEND action, type: ${intent.type}")
|
||||
when (intent.type) {
|
||||
"application/octet-stream" -> {
|
||||
intent.getUriCompat()?.let { uri ->
|
||||
|
@ -60,7 +60,7 @@ class ImportConfigActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
ACTION_VIEW -> {
|
||||
Log.v(TAG, "Process VIEW action, scheme: ${intent.scheme}")
|
||||
Log.d(TAG, "Process VIEW action, scheme: ${intent.scheme}")
|
||||
when (intent.scheme) {
|
||||
"file", "content" -> {
|
||||
intent.data?.let { uri ->
|
||||
|
@ -128,7 +128,7 @@ class ImportConfigActivity : ComponentActivity() {
|
|||
|
||||
private fun startMainActivity(config: String) {
|
||||
if (config.isNotBlank()) {
|
||||
Log.v(TAG, "startMainActivity")
|
||||
Log.d(TAG, "startMainActivity")
|
||||
Intent(applicationContext, AmneziaActivity::class.java).apply {
|
||||
action = ACTION_IMPORT_CONFIG
|
||||
addCategory(CATEGORY_DEFAULT)
|
||||
|
|
|
@ -32,7 +32,8 @@ enum class Action : IpcMessage {
|
|||
REGISTER_CLIENT,
|
||||
CONNECT,
|
||||
DISCONNECT,
|
||||
REQUEST_STATUS
|
||||
REQUEST_STATUS,
|
||||
SET_SAVE_LOGS
|
||||
}
|
||||
|
||||
fun <T> T.packToMessage(): Message
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package org.amnezia.vpn
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
import org.amnezia.vpn.util.Log
|
||||
|
||||
private const val TAG = "Prefs"
|
||||
private const val PREFS_FILE = "org.amnezia.vpn.prefs"
|
||||
private const val SECURE_PREFS_FILE = "$PREFS_FILE.secure"
|
||||
|
||||
object Prefs {
|
||||
fun get(context: Context, appContext: Context = context.applicationContext): SharedPreferences =
|
||||
try {
|
||||
EncryptedSharedPreferences(
|
||||
appContext,
|
||||
SECURE_PREFS_FILE,
|
||||
MasterKey(appContext)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Getting Encryption Storage failed: ${e.message}, plaintext fallback")
|
||||
appContext.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ class VpnRequestActivity : ComponentActivity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.v(TAG, "Start request activity")
|
||||
Log.d(TAG, "Start request activity")
|
||||
val requestIntent = VpnService.prepare(applicationContext)
|
||||
if (requestIntent != null) {
|
||||
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
|
||||
|
|
|
@ -15,3 +15,7 @@ android {
|
|||
buildConfig = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.security.crypto)
|
||||
}
|
||||
|
|
|
@ -1,33 +1,252 @@
|
|||
package org.amnezia.vpn.util
|
||||
|
||||
import android.content.Context
|
||||
import android.icu.text.DateFormat
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.os.Build
|
||||
import android.os.Process
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.channels.FileChannel
|
||||
import java.nio.channels.FileLock
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import org.amnezia.vpn.util.Log.Priority.D
|
||||
import org.amnezia.vpn.util.Log.Priority.E
|
||||
import org.amnezia.vpn.util.Log.Priority.F
|
||||
import org.amnezia.vpn.util.Log.Priority.I
|
||||
import org.amnezia.vpn.util.Log.Priority.V
|
||||
import org.amnezia.vpn.util.Log.Priority.W
|
||||
import android.util.Log as NativeLog
|
||||
|
||||
class Log {
|
||||
companion object {
|
||||
fun v(tag: String, msg: String) = debugLog(tag, msg, NativeLog::v)
|
||||
private const val TAG = "Log"
|
||||
private const val LOG_FILE_NAME = "amneziaVPN.log"
|
||||
private const val ROTATE_LOG_FILE_NAME = "amneziaVPN.rotate.log"
|
||||
private const val LOCK_FILE_NAME = ".lock"
|
||||
private const val DATE_TIME_PATTERN = "MM-dd HH:mm:ss.SSS"
|
||||
private const val PREFS_SAVE_LOGS_KEY = "SAVE_LOGS"
|
||||
private const val LOG_MAX_FILE_SIZE = 1024 * 1024
|
||||
|
||||
fun d(tag: String, msg: String) = debugLog(tag, msg, NativeLog::d)
|
||||
/**
|
||||
* | Priority | Save to file | Logcat logging |
|
||||
* |-------------------|--------------|----------------------------------------------|
|
||||
* | Verbose | Don't save | Only in Debug build |
|
||||
* | Debug | Save | In Debug build or if log saving is enabled |
|
||||
* | Info, Warn, Error | Save | Enabled |
|
||||
* | Fatal (Assert) | Save | Enabled. Depending on system configuration, |
|
||||
* | | | create a report and/or terminate the process |
|
||||
*/
|
||||
object Log {
|
||||
private val dateTimeFormat: Any =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
|
||||
else object : ThreadLocal<DateFormat>() {
|
||||
override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US)
|
||||
}
|
||||
|
||||
fun i(tag: String, msg: String) = log(tag, msg, NativeLog::i)
|
||||
private lateinit var logDir: File
|
||||
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
|
||||
private val rotateLogFile: File by lazy { File(logDir, ROTATE_LOG_FILE_NAME) }
|
||||
|
||||
fun w(tag: String, msg: String) = log(tag, msg, NativeLog::w)
|
||||
private val fileLock: FileChannel by lazy { RandomAccessFile(File(logDir, LOCK_FILE_NAME).path, "rw").channel }
|
||||
private val threadLock: ReentrantLock by lazy { ReentrantLock() }
|
||||
|
||||
fun e(tag: String, msg: String) = log(tag, msg, NativeLog::e)
|
||||
@Volatile
|
||||
private var _saveLogs: Boolean = false
|
||||
var saveLogs: Boolean
|
||||
get() = _saveLogs
|
||||
set(value) {
|
||||
if (_saveLogs != value) {
|
||||
if (value && !logDir.exists() && !logDir.mkdir()) {
|
||||
NativeLog.e(TAG, "Failed to create dir: $logDir")
|
||||
return
|
||||
}
|
||||
_saveLogs = value
|
||||
Prefs.save(PREFS_SAVE_LOGS_KEY, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun v(tag: String, msg: Any?) = v(tag, msg.toString())
|
||||
@JvmStatic
|
||||
fun v(tag: String, msg: String) = log(tag, msg, V)
|
||||
|
||||
fun d(tag: String, msg: Any?) = d(tag, msg.toString())
|
||||
@JvmStatic
|
||||
fun d(tag: String, msg: String) = log(tag, msg, D)
|
||||
|
||||
fun i(tag: String, msg: Any?) = i(tag, msg.toString())
|
||||
@JvmStatic
|
||||
fun i(tag: String, msg: String) = log(tag, msg, I)
|
||||
|
||||
fun w(tag: String, msg: Any?) = w(tag, msg.toString())
|
||||
@JvmStatic
|
||||
fun w(tag: String, msg: String) = log(tag, msg, W)
|
||||
|
||||
fun e(tag: String, msg: Any?) = e(tag, msg.toString())
|
||||
@JvmStatic
|
||||
fun e(tag: String, msg: String) = log(tag, msg, E)
|
||||
|
||||
private inline fun log(tag: String, msg: String, delegate: (String, String) -> Unit) = delegate(tag, msg)
|
||||
@JvmStatic
|
||||
fun f(tag: String, msg: String) = log(tag, msg, F)
|
||||
|
||||
private inline fun debugLog(tag: String, msg: String, delegate: (String, String) -> Unit) {
|
||||
if (BuildConfig.DEBUG) delegate(tag, msg)
|
||||
fun v(tag: String, msg: Any?) = v(tag, msg.toString())
|
||||
|
||||
fun d(tag: String, msg: Any?) = d(tag, msg.toString())
|
||||
|
||||
fun i(tag: String, msg: Any?) = i(tag, msg.toString())
|
||||
|
||||
fun w(tag: String, msg: Any?) = w(tag, msg.toString())
|
||||
|
||||
fun e(tag: String, msg: Any?) = e(tag, msg.toString())
|
||||
|
||||
fun f(tag: String, msg: Any?) = f(tag, msg.toString())
|
||||
|
||||
fun init(context: Context) {
|
||||
v(TAG, "Init Log")
|
||||
logDir = File(context.cacheDir, "logs")
|
||||
saveLogs = Prefs.load(PREFS_SAVE_LOGS_KEY)
|
||||
}
|
||||
|
||||
fun getLogs(): String =
|
||||
"${deviceInfo()}\n${readLogs()}\nLOGCAT:\n${getLogcat()}"
|
||||
|
||||
fun clearLogs() {
|
||||
withLock {
|
||||
logFile.delete()
|
||||
rotateLogFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun log(tag: String, msg: String, priority: Priority) {
|
||||
if (saveLogs && priority != V) saveLogMsg(formatLogMsg(tag, msg, priority))
|
||||
|
||||
if (priority == F) {
|
||||
NativeLog.wtf(tag, msg)
|
||||
} else if (
|
||||
(priority != V && priority != D) ||
|
||||
(priority == V && BuildConfig.DEBUG) ||
|
||||
(priority == D && (BuildConfig.DEBUG || saveLogs))
|
||||
) {
|
||||
NativeLog.println(priority.level, tag, msg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveLogMsg(msg: String) {
|
||||
withTryLock(condition = { logFile.length() > LOG_MAX_FILE_SIZE }) {
|
||||
logFile.renameTo(rotateLogFile)
|
||||
}
|
||||
try {
|
||||
logFile.appendText(msg)
|
||||
} catch (e: IOException) {
|
||||
NativeLog.e(TAG, "Failed to write log: $e")
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
||||
val date = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
LocalDateTime.now().format(dateTimeFormat as DateTimeFormatter)
|
||||
} else {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(dateTimeFormat as ThreadLocal<DateFormat>).get()?.format(Date())
|
||||
}
|
||||
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
||||
"$tag: $msg\n"
|
||||
}
|
||||
|
||||
private fun deviceInfo(): String {
|
||||
val sb = StringBuilder()
|
||||
sb.append("Model: ").appendLine(Build.MODEL)
|
||||
sb.append("Brand: ").appendLine(Build.BRAND)
|
||||
sb.append("Product: ").appendLine(Build.PRODUCT)
|
||||
sb.append("Device: ").appendLine(Build.DEVICE)
|
||||
sb.append("Codename: ").appendLine(Build.VERSION.CODENAME)
|
||||
sb.append("Release: ").appendLine(Build.VERSION.RELEASE)
|
||||
sb.append("SDK: ").appendLine(Build.VERSION.SDK_INT)
|
||||
sb.append("ABI: ").appendLine(Build.SUPPORTED_ABIS.joinToString())
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun readLogs(): String {
|
||||
var logText = ""
|
||||
withLock {
|
||||
try {
|
||||
if (rotateLogFile.exists()) logText = rotateLogFile.readText()
|
||||
if (logFile.exists()) logText += logFile.readText()
|
||||
} catch (e: IOException) {
|
||||
val errorMsg = "Failed to read log: $e"
|
||||
NativeLog.e(TAG, errorMsg)
|
||||
logText += errorMsg
|
||||
}
|
||||
}
|
||||
return logText
|
||||
}
|
||||
|
||||
private fun getLogcat(): String {
|
||||
try {
|
||||
val process = ProcessBuilder("logcat", "-d").redirectErrorStream(true).start()
|
||||
return process.inputStream.reader().readText()
|
||||
} catch (e: IOException) {
|
||||
val errorMsg = "Failed to get logcat log: $e"
|
||||
NativeLog.e(TAG, errorMsg)
|
||||
return errorMsg
|
||||
}
|
||||
}
|
||||
|
||||
private fun withLock(block: () -> Unit) {
|
||||
threadLock.lock()
|
||||
try {
|
||||
var l: FileLock? = null
|
||||
try {
|
||||
l = fileLock.lock()
|
||||
block()
|
||||
} catch (e: IOException) {
|
||||
NativeLog.e(TAG, "Failed to get file lock: $e")
|
||||
} finally {
|
||||
try {
|
||||
l?.release()
|
||||
} catch (e: IOException) {
|
||||
NativeLog.e(TAG, "Failed to release file lock: $e")
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
threadLock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
private fun withTryLock(condition: () -> Boolean, block: () -> Unit) {
|
||||
if (condition()) {
|
||||
if (threadLock.tryLock()) {
|
||||
try {
|
||||
if (condition()) {
|
||||
var l: FileLock? = null
|
||||
try {
|
||||
l = fileLock.tryLock()
|
||||
if (l != null) {
|
||||
if (condition()) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
NativeLog.e(TAG, "Failed to get file tryLock: $e")
|
||||
} finally {
|
||||
try {
|
||||
l?.release()
|
||||
} catch (e: IOException) {
|
||||
NativeLog.e(TAG, "Failed to release file tryLock: $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
threadLock.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Priority(val level: Int) {
|
||||
V(2),
|
||||
D(3),
|
||||
I(4),
|
||||
W(5),
|
||||
E(6),
|
||||
F(7)
|
||||
}
|
||||
}
|
||||
|
|
58
client/android/utils/src/main/kotlin/Prefs.kt
Normal file
58
client/android/utils/src/main/kotlin/Prefs.kt
Normal file
|
@ -0,0 +1,58 @@
|
|||
package org.amnezia.vpn.util
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
private const val TAG = "Prefs"
|
||||
private const val PREFS_FILE = "org.amnezia.vpn.prefs"
|
||||
private const val SECURE_PREFS_FILE = "$PREFS_FILE.secure"
|
||||
|
||||
object Prefs {
|
||||
private lateinit var app: Application
|
||||
val prefs: SharedPreferences
|
||||
get() = try {
|
||||
EncryptedSharedPreferences(
|
||||
app,
|
||||
SECURE_PREFS_FILE,
|
||||
MasterKey(app)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Getting Encryption Storage failed: $e, plaintext fallback")
|
||||
app.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
fun init(app: Application) {
|
||||
Log.v(TAG, "Init Prefs")
|
||||
this.app = app
|
||||
}
|
||||
|
||||
fun save(key: String, value: Boolean) =
|
||||
prefs.edit().putBoolean(key, value).apply()
|
||||
|
||||
fun save(key: String, value: String?) =
|
||||
prefs.edit().putString(key, value).apply()
|
||||
|
||||
fun save(key: String, value: Int) =
|
||||
prefs.edit().putInt(key, value).apply()
|
||||
|
||||
fun save(key: String, value: Long) =
|
||||
prefs.edit().putLong(key, value).apply()
|
||||
|
||||
fun save(key: String, value: Float) =
|
||||
prefs.edit().putFloat(key, value).apply()
|
||||
|
||||
inline fun <reified T> load(key: String): T {
|
||||
return when (typeOf<T>()) {
|
||||
typeOf<Boolean>() -> prefs.getBoolean(key, false)
|
||||
typeOf<String>() -> prefs.getString(key, "")
|
||||
typeOf<Int>() -> prefs.getInt(key, 0)
|
||||
typeOf<Long>() -> prefs.getLong(key, 0L)
|
||||
typeOf<Float>() -> prefs.getFloat(key, 0f)
|
||||
else -> throw IllegalArgumentException("SharedPreferences does not support type: ${typeOf<T>()}")
|
||||
} as T
|
||||
}
|
||||
}
|
|
@ -82,7 +82,7 @@ class NetworkState(
|
|||
|
||||
fun bindNetworkListener() {
|
||||
if (isListenerBound) return
|
||||
Log.v(TAG, "Bind network listener")
|
||||
Log.d(TAG, "Bind network listener")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
@ -95,7 +95,7 @@ class NetworkState(
|
|||
|
||||
fun unbindNetworkListener() {
|
||||
if (!isListenerBound) return
|
||||
Log.v(TAG, "Unbind network listener")
|
||||
Log.d(TAG, "Unbind network listener")
|
||||
connectivityManager.unregisterNetworkCallback(networkCallback)
|
||||
isListenerBound = false
|
||||
currentNetwork = null
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue