diff options
Diffstat (limited to 'ui/src/main/java/com/wireguard/android/Application.kt')
-rw-r--r-- | ui/src/main/java/com/wireguard/android/Application.kt | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/ui/src/main/java/com/wireguard/android/Application.kt b/ui/src/main/java/com/wireguard/android/Application.kt new file mode 100644 index 00000000..4dcec508 --- /dev/null +++ b/ui/src/main/java/com/wireguard/android/Application.kt @@ -0,0 +1,157 @@ +/* + * Copyright © 2017-2023 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.google.android.material.color.DynamicColors +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.updater.Updater +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 kotlinx.coroutines.runBlocking +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 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) + } + } + + private suspend fun determineBackend(): Backend { + var backend: Backend? = null + if (UserKnobs.enableKernelModule.first() && WgQuickBackend.hasKernelSupport()) { + try { + 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() + DynamicColors.applyToActivitiesIfAvailable(this) + rootShell = RootShell(applicationContext) + toolsInstaller = ToolsInstaller(applicationContext, rootShell) + preferencesDataStore = PreferenceDataStoreFactory.create { applicationContext.preferencesDataStoreFile("settings") } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + runBlocking { + AppCompatDelegate.setDefaultNightMode(if (UserKnobs.darkTheme.first()) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO) + } + UserKnobs.darkTheme.onEach { + val newMode = if (it) { + AppCompatDelegate.MODE_NIGHT_YES + } else { + AppCompatDelegate.MODE_NIGHT_NO + } + if (AppCompatDelegate.getDefaultNightMode() != newMode) { + AppCompatDelegate.setDefaultNightMode(newMode) + } + }.launchIn(coroutineScope) + } 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)) + } + } + Updater.monitorForUpdates() + + if (BuildConfig.DEBUG) { + StrictMode.setVmPolicy(VmPolicy.Builder().detectAll().penaltyLog().build()) + StrictMode.setThreadPolicy(ThreadPolicy.Builder().detectAll().penaltyLog().build()) + } + } + + 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> + + fun get(): Application { + return weakSelf.get()!! + } + + suspend fun getBackend() = get().futureBackend.await() + + fun getRootShell() = get().rootShell + + fun getPreferencesDataStore() = get().preferencesDataStore + + fun getToolsInstaller() = get().toolsInstaller + + fun getTunnelManager() = get().tunnelManager + + fun getCoroutineScope() = get().coroutineScope + } + + init { + weakSelf = WeakReference(this) + } +} |