/* * 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.content.SharedPreferences import android.content.SharedPreferences.OnSharedPreferenceChangeListener 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.preference.PreferenceManager 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 kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import java.lang.ref.WeakReference import java.util.Locale class Application : android.app.Application(), OnSharedPreferenceChangeListener { private val futureBackend = CompletableDeferred() 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 sharedPreferences: SharedPreferences 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 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 (!sharedPreferences.getBoolean("disable_kernel_module", false) && ModuleLoader.isModuleLoaded()) { try { if (!didStartRootShell) rootShell.start() val wgQuickBackend = WgQuickBackend(applicationContext, rootShell, toolsInstaller) wgQuickBackend.setMultipleTunnels(sharedPreferences.getBoolean("multiple_tunnels", false)) backend = wgQuickBackend } catch (ignored: Exception) { } } if (backend == null) { backend = GoBackend(applicationContext) GoBackend.setAlwaysOnCallback { 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) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { AppCompatDelegate.setDefaultNightMode( if (sharedPreferences.getBoolean("dark_theme", false)) 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)) } } sharedPreferences.registerOnSharedPreferenceChangeListener(this) } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { if ("multiple_tunnels" == key && backend != null && backend is WgQuickBackend) (backend as WgQuickBackend).setMultipleTunnels(sharedPreferences.getBoolean(key, false)) } override fun onTerminate() { sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) 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 @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 getSharedPreferences() = get().sharedPreferences @JvmStatic fun getToolsInstaller() = get().toolsInstaller @JvmStatic fun getTunnelManager() = get().tunnelManager @JvmStatic fun getCoroutineScope() = get().coroutineScope } init { weakSelf = WeakReference(this) } }