owenclave / app/src/main/java/io/nekohasekai/sagernet/SagerNet.kt

github - dev - back to tree - upstream
/****************************************************************************** * * * Copyright (C) 2021 by nekohasekai * * Copyright (C) 2021 by Max Lv * * Copyright (C) 2021 by Mygod Studio * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ******************************************************************************/ package io.nekohasekai.sagernet import android.annotation.SuppressLint import android.app.* import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.res.Configuration import android.location.LocationManager import android.net.ConnectivityManager import android.net.LinkAddress import android.net.Network import android.net.NetworkCapabilities import android.net.wifi.WifiManager import android.os.Build import android.os.PowerManager import android.os.StrictMode import android.util.Log import android.view.inputmethod.InputMethodManager import android.widget.Toast import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import go.Seq import io.nekohasekai.sagernet.bg.SagerConnection import io.nekohasekai.sagernet.bg.SubscriptionUpdater import io.nekohasekai.sagernet.bg.test.DebugInstance import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.SagerDatabase import io.nekohasekai.sagernet.ktx.Logs import io.nekohasekai.sagernet.ktx.readableMessage import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher import io.nekohasekai.sagernet.ui.MainActivity import io.nekohasekai.sagernet.utils.CrashHandler import io.nekohasekai.sagernet.utils.DefaultNetworkListener import io.nekohasekai.sagernet.utils.DeviceStorageApp import io.nekohasekai.sagernet.utils.PackageCache import io.nekohasekai.sagernet.utils.Theme import kotlinx.coroutines.DEBUG_PROPERTY_NAME import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON import libexclavecore.Libexclavecore import libexclavecore.UidDumper import java.net.Inet6Address import java.net.InetSocketAddress import androidx.work.Configuration as WorkConfiguration class SagerNet : Application(), UidDumper, WorkConfiguration.Provider { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) application = this } val externalAssets by lazy { getExternalFilesDir(null) ?: filesDir } override fun onCreate() { super.onCreate() System.setProperty(DEBUG_PROPERTY_NAME, DEBUG_PROPERTY_VALUE_ON) Thread.setDefaultUncaughtExceptionHandler(CrashHandler) DataStore.init() updateNotificationChannels() Seq.setContext(this) if (DataStore.getInstalledPackagesInited) { runOnDefaultDispatcher { PackageCache.register() } } val processName = if (Build.VERSION.SDK_INT >= 28) { getProcessName() } else { @SuppressLint("PrivateApi") try { Class.forName("android.app.ActivityThread").getDeclaredMethod("currentProcessName").invoke(null) as String } catch (_: Exception) { BuildConfig.APPLICATION_ID } } val isMainProcess = processName == BuildConfig.APPLICATION_ID if (!isMainProcess) { Libexclavecore.setUidDumper(this, Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) if (DataStore.enableDebug && DataStore.pprofServer.isNotEmpty()) { DebugInstance().launch() } } if (!isMainProcess) runOnDefaultDispatcher { runCatching { SubscriptionUpdater.reconfigureUpdater() } } Libexclavecore.setenv("owenclave.conf.geoloader", "memconservative") externalAssets.mkdirs() Libexclavecore.initializeV2Ray( filesDir.absolutePath + "/", externalAssets.absolutePath + "/", "owenclave-core/", ) try { Libexclavecore.updateSystemRoots(DataStore.providerRootCA) } catch (e: Exception) { Toast.makeText(this, e.readableMessage, Toast.LENGTH_LONG).show() } Theme.apply(this) Theme.applyNightTheme() if (BuildConfig.DEBUG) StrictMode.setVmPolicy( StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .detectLeakedRegistrationObjects() .penaltyLog() .build() ) } @RequiresApi(Build.VERSION_CODES.Q) override fun dumpUid( ipProto: Int, srcIp: String, srcPort: Int, destIp: String, destPort: Int ): Int { return connectivity.getConnectionOwnerUid( ipProto, InetSocketAddress(srcIp, srcPort), InetSocketAddress(destIp, destPort) ) } override fun getPackageName(uid: Int): String? { PackageCache.awaitLoadSync() if (uid == 1000) { return "android" } val packageNames = PackageCache.uidMap[uid] if (!packageNames.isNullOrEmpty()) for (packageName in packageNames) { return packageName } return null } fun getPackageInfo(packageName: String) = packageManager.getPackageInfo( packageName, if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES else @Suppress("DEPRECATION") PackageManager.GET_SIGNATURES )!! override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) updateNotificationChannels() } override val workManagerConfiguration: androidx.work.Configuration get() { return WorkConfiguration.Builder() // .setDefaultProcessName("${BuildConfig.APPLICATION_ID}:bg") .setDefaultProcessName(BuildConfig.APPLICATION_ID) .build() } @SuppressLint("InlinedApi") companion object { @Volatile var started = false lateinit var application: SagerNet val isTv by lazy { uiMode.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION } val deviceStorage by lazy { if (Build.VERSION.SDK_INT < 24) application else DeviceStorageApp(application) } val configureIntent: (Context) -> PendingIntent by lazy { { PendingIntent.getActivity( it, 0, Intent( application, MainActivity::class.java ).setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0 ) } } val activity by lazy { application.getSystemService()!! } val clipboard by lazy { application.getSystemService()!! } val connectivity by lazy { application.getSystemService()!! } val ime by lazy { application.getSystemService()!! } val notification by lazy { application.getSystemService()!! } val uiMode by lazy { application.getSystemService()!! } val power by lazy { application.getSystemService()!! } val wifi by lazy { application.getSystemService()!! } val location by lazy { application.getSystemService()!! } val locale by lazy { application.getSystemService()!! } val currentProfile get() = SagerDatabase.proxyDao.getById(DataStore.selectedProxy) fun getClipboardText(): String { return clipboard.primaryClip?.takeIf { it.itemCount > 0 } ?.getItemAt(0)?.text?.toString() ?: "" } fun trySetPrimaryClip(clip: String) = try { clipboard.setPrimaryClip(ClipData.newPlainText(null, clip)) true } catch (e: RuntimeException) { Logs.w(e) false } fun updateNotificationChannels() { if (Build.VERSION.SDK_INT >= 26) @RequiresApi(26) { notification.createNotificationChannels( listOf( NotificationChannel( "service-vpn", application.getText(R.string.service_vpn), if (Build.VERSION.SDK_INT >= 28) NotificationManager.IMPORTANCE_MIN else NotificationManager.IMPORTANCE_LOW ), // #1355 NotificationChannel( "service-proxy", application.getText(R.string.service_proxy), NotificationManager.IMPORTANCE_LOW ), NotificationChannel( "service-subscription", application.getText(R.string.service_subscription), NotificationManager.IMPORTANCE_DEFAULT ) ) ) } } var currentNetwork: Network? = null var currentLinkAddresses: Set? = null fun reloadNetwork(network: Network?) { val capabilities = connectivity.getNetworkCapabilities(network) ?: return val linkProperties = connectivity.getLinkProperties(network) ?: return val networkType = when { capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "wifi" capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE) -> "wifi" capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "data" capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> "bluetooth" capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> "ethernet" capabilities.hasTransport(NetworkCapabilities.TRANSPORT_USB) -> "usb" capabilities.hasTransport(NetworkCapabilities.TRANSPORT_SATELLITE) -> "satellite" // capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE) -> "wifiaware" // capabilities.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN) -> "lowpan" // capabilities.hasTransport(NetworkCapabilities.TRANSPORT_THREAD) -> "thread" else -> "" } Libexclavecore.setNetworkType(networkType) var ssid = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { DefaultNetworkListener.ssid ?: "" } else { @Suppress("DEPRECATION") wifi.connectionInfo?.ssid ?: "" } if (ssid == WifiManager.UNKNOWN_SSID) ssid = "" if (ssid.length >= 2 && ssid.first() == '\"' && ssid.last() == '\"') { ssid = ssid.substring(1, ssid.length - 1) } Libexclavecore.setSSID(ssid) val linkAddresses = linkProperties.linkAddresses.toSet() if (DataStore.logLevel == LogLevel.DEBUG && currentLinkAddresses != linkAddresses) { Log.d("Owenclave", "updated link addresses: " + linkAddresses.joinToString(" ", "[", "]") { it.address.hostAddress!! + "/" + it.prefixLength }) } Libexclavecore.setDiscardIPv6(!linkAddresses.any { it.address is Inet6Address && !it.address.isLinkLocalAddress }) if (DataStore.interruptReusedConnections) { val networkChanged = currentNetwork != null && currentNetwork != network val linkAddressesChanged = currentLinkAddresses != null && !linkAddresses.containsAll(currentLinkAddresses!!) if (networkChanged || linkAddressesChanged) { if (DataStore.logLevel == LogLevel.DEBUG) { Log.d("Owenclave", "network changed, interrupt reused connections") } Libexclavecore.interfaceUpdate() } } currentNetwork = network currentLinkAddresses = linkAddresses } fun startService() = ContextCompat.startForegroundService( application, Intent(application, SagerConnection.serviceClass) ) fun reloadService() = application.sendBroadcast(Intent(Action.RELOAD).setPackage(application.packageName)) fun stopService() = application.sendBroadcast(Intent(Action.CLOSE).setPackage(application.packageName)) } }