166 lines
6.3 KiB
Kotlin
166 lines
6.3 KiB
Kotlin
/*
|
|
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
package com.wireguard.android
|
|
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.os.Build
|
|
import android.os.StrictMode
|
|
import android.os.StrictMode.ThreadPolicy
|
|
import android.os.StrictMode.VmPolicy
|
|
import android.util.Log
|
|
import androidx.appcompat.app.AppCompatDelegate
|
|
import androidx.datastore.core.DataStore
|
|
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
|
import androidx.datastore.preferences.core.Preferences
|
|
import androidx.datastore.preferences.preferencesDataStoreFile
|
|
import com.wireguard.android.backend.Backend
|
|
import com.wireguard.android.backend.GoBackend
|
|
import com.wireguard.android.backend.WgQuickBackend
|
|
import com.wireguard.android.configStore.FileConfigStore
|
|
import com.wireguard.android.model.TunnelManager
|
|
import com.wireguard.android.util.ModuleLoader
|
|
import com.wireguard.android.util.RootShell
|
|
import com.wireguard.android.util.ToolsInstaller
|
|
import com.wireguard.android.util.UserKnobs
|
|
import com.wireguard.android.util.applicationScope
|
|
import kotlinx.coroutines.CompletableDeferred
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.Job
|
|
import kotlinx.coroutines.cancel
|
|
import kotlinx.coroutines.flow.first
|
|
import kotlinx.coroutines.flow.launchIn
|
|
import kotlinx.coroutines.flow.onEach
|
|
import kotlinx.coroutines.launch
|
|
import java.lang.ref.WeakReference
|
|
import java.util.Locale
|
|
|
|
class Application : android.app.Application() {
|
|
private val futureBackend = CompletableDeferred<Backend>()
|
|
private val coroutineScope = CoroutineScope(Job() + Dispatchers.Main.immediate)
|
|
private var backend: Backend? = null
|
|
private lateinit var moduleLoader: ModuleLoader
|
|
private lateinit var rootShell: RootShell
|
|
private lateinit var preferencesDataStore: DataStore<Preferences>
|
|
private lateinit var toolsInstaller: ToolsInstaller
|
|
private lateinit var tunnelManager: TunnelManager
|
|
|
|
override fun attachBaseContext(context: Context) {
|
|
super.attachBaseContext(context)
|
|
if (BuildConfig.MIN_SDK_VERSION > Build.VERSION.SDK_INT) {
|
|
val intent = Intent(Intent.ACTION_MAIN)
|
|
intent.addCategory(Intent.CATEGORY_HOME)
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
startActivity(intent)
|
|
System.exit(0)
|
|
}
|
|
if (BuildConfig.DEBUG) {
|
|
StrictMode.setVmPolicy(VmPolicy.Builder().detectAll().penaltyLog().build())
|
|
StrictMode.setThreadPolicy(ThreadPolicy.Builder().detectAll().penaltyLog().build())
|
|
}
|
|
}
|
|
|
|
private suspend fun determineBackend(): Backend {
|
|
var backend: Backend? = null
|
|
var didStartRootShell = false
|
|
if (!ModuleLoader.isModuleLoaded() && moduleLoader.moduleMightExist()) {
|
|
try {
|
|
rootShell.start()
|
|
didStartRootShell = true
|
|
moduleLoader.loadModule()
|
|
} catch (ignored: Exception) {
|
|
}
|
|
}
|
|
if (!UserKnobs.disableKernelModule.first() && ModuleLoader.isModuleLoaded()) {
|
|
try {
|
|
if (!didStartRootShell)
|
|
rootShell.start()
|
|
val wgQuickBackend = WgQuickBackend(applicationContext, rootShell, toolsInstaller)
|
|
wgQuickBackend.setMultipleTunnels(UserKnobs.multipleTunnels.first())
|
|
backend = wgQuickBackend
|
|
UserKnobs.multipleTunnels.onEach {
|
|
wgQuickBackend.setMultipleTunnels(it)
|
|
}.launchIn(coroutineScope)
|
|
} catch (ignored: Exception) {
|
|
}
|
|
}
|
|
if (backend == null) {
|
|
backend = GoBackend(applicationContext)
|
|
GoBackend.setAlwaysOnCallback { get().applicationScope.launch { get().tunnelManager.restoreState(true) } }
|
|
}
|
|
return backend
|
|
}
|
|
|
|
override fun onCreate() {
|
|
Log.i(TAG, USER_AGENT)
|
|
super.onCreate()
|
|
rootShell = RootShell(applicationContext)
|
|
toolsInstaller = ToolsInstaller(applicationContext, rootShell)
|
|
moduleLoader = ModuleLoader(applicationContext, rootShell, USER_AGENT)
|
|
preferencesDataStore = PreferenceDataStoreFactory.create { applicationContext.preferencesDataStoreFile("settings") }
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
coroutineScope.launch {
|
|
AppCompatDelegate.setDefaultNightMode(
|
|
if (UserKnobs.darkTheme.first()) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
|
|
}
|
|
} else {
|
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
|
}
|
|
tunnelManager = TunnelManager(FileConfigStore(applicationContext))
|
|
tunnelManager.onCreate()
|
|
coroutineScope.launch(Dispatchers.IO) {
|
|
try {
|
|
backend = determineBackend()
|
|
futureBackend.complete(backend!!)
|
|
} catch (e: Throwable) {
|
|
Log.e(TAG, Log.getStackTraceString(e))
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onTerminate() {
|
|
coroutineScope.cancel()
|
|
super.onTerminate()
|
|
}
|
|
|
|
companion object {
|
|
val USER_AGENT = String.format(Locale.ENGLISH, "WireGuard/%s (Android %d; %s; %s; %s %s; %s)", BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT, if (Build.SUPPORTED_ABIS.isNotEmpty()) Build.SUPPORTED_ABIS[0] else "unknown ABI", Build.BOARD, Build.MANUFACTURER, Build.MODEL, Build.FINGERPRINT)
|
|
private const val TAG = "WireGuard/Application"
|
|
private lateinit var weakSelf: WeakReference<Application>
|
|
|
|
@JvmStatic
|
|
fun get(): Application {
|
|
return weakSelf.get()!!
|
|
}
|
|
|
|
@JvmStatic
|
|
suspend fun getBackend() = get().futureBackend.await()
|
|
|
|
@JvmStatic
|
|
fun getModuleLoader() = get().moduleLoader
|
|
|
|
@JvmStatic
|
|
fun getRootShell() = get().rootShell
|
|
|
|
@JvmStatic
|
|
fun getPreferencesDataStore() = get().preferencesDataStore
|
|
|
|
@JvmStatic
|
|
fun getToolsInstaller() = get().toolsInstaller
|
|
|
|
@JvmStatic
|
|
fun getTunnelManager() = get().tunnelManager
|
|
|
|
@JvmStatic
|
|
fun getCoroutineScope() = get().coroutineScope
|
|
}
|
|
|
|
init {
|
|
weakSelf = WeakReference(this)
|
|
}
|
|
}
|