Commit 85c23a12 authored by Mygod's avatar Mygod

Avoid resolving every plugin in :bg

parent 6e4dd864
...@@ -242,7 +242,7 @@ object BaseService { ...@@ -242,7 +242,7 @@ object BaseService {
File(Core.deviceStorage.noBackupFilesDir, "stat_main"), File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
File(configRoot, CONFIG_FILE), File(configRoot, CONFIG_FILE),
if (udpFallback == null) "-u" else null) if (udpFallback == null) "-u" else null)
check(udpFallback?.pluginPath == null) { "UDP fallback cannot have plugins" } check(udpFallback?.plugin == null) { "UDP fallback cannot have plugins" }
udpFallback?.start(this, udpFallback?.start(this,
File(Core.deviceStorage.noBackupFilesDir, "stat_udp"), File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
File(configRoot, CONFIG_FILE_UDP), File(configRoot, CONFIG_FILE_UDP),
......
...@@ -47,8 +47,7 @@ import java.security.MessageDigest ...@@ -47,8 +47,7 @@ import java.security.MessageDigest
class ProxyInstance(val profile: Profile, private val route: String = profile.route) { class ProxyInstance(val profile: Profile, private val route: String = profile.route) {
private var configFile: File? = null private var configFile: File? = null
var trafficMonitor: TrafficMonitor? = null var trafficMonitor: TrafficMonitor? = null
private val plugin = PluginConfiguration(profile.plugin ?: "").selectedOptions val plugin by lazy { PluginManager.init(PluginConfiguration(profile.plugin ?: "")) }
val pluginPath by lazy { PluginManager.init(plugin) }
private var scheduleConfigUpdate = false private var scheduleConfigUpdate = false
suspend fun init(service: BaseService.Interface, hosts: HostsFile) { suspend fun init(service: BaseService.Interface, hosts: HostsFile) {
...@@ -115,7 +114,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -115,7 +114,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
this.configFile = configFile this.configFile = configFile
val config = profile.toJson() val config = profile.toJson()
if (pluginPath != null) config.put("plugin", pluginPath).put("plugin_opts", plugin.toString()) plugin?.let { (path, opts) -> config.put("plugin", path).put("plugin_opts", opts.toString()) }
configFile.writeText(config.toString()) configFile.writeText(config.toString())
val cmd = service.buildAdditionalArguments(arrayListOf( val cmd = service.buildAdditionalArguments(arrayListOf(
......
...@@ -301,7 +301,7 @@ data class Profile( ...@@ -301,7 +301,7 @@ data class Profile(
.encodedAuthority("$auth@$wrappedHost:$remotePort") .encodedAuthority("$auth@$wrappedHost:$remotePort")
val configuration = PluginConfiguration(plugin ?: "") val configuration = PluginConfiguration(plugin ?: "")
if (configuration.selected.isNotEmpty()) { if (configuration.selected.isNotEmpty()) {
builder.appendQueryParameter(Key.plugin, configuration.selectedOptions.toString(false)) builder.appendQueryParameter(Key.plugin, configuration.getOptions().toString(false))
} }
if (!name.isNullOrEmpty()) builder.fragment(name) if (!name.isNullOrEmpty()) builder.fragment(name)
return builder.build() return builder.build()
...@@ -315,7 +315,7 @@ data class Profile( ...@@ -315,7 +315,7 @@ data class Profile(
put("password", password) put("password", password)
put("method", method) put("method", method)
if (profiles == null) return@apply if (profiles == null) return@apply
PluginConfiguration(plugin ?: "").selectedOptions.also { PluginConfiguration(plugin ?: "").getOptions().also {
if (it.id.isNotEmpty()) { if (it.id.isNotEmpty()) {
put("plugin", it.id) put("plugin", it.id)
put("plugin_opts", it.toString()) put("plugin_opts", it.toString())
......
...@@ -50,15 +50,15 @@ class PluginConfiguration(val pluginsOptions: Map<String, PluginOptions>, val se ...@@ -50,15 +50,15 @@ class PluginConfiguration(val pluginsOptions: Map<String, PluginOptions>, val se
} else PluginOptions(line) } else PluginOptions(line)
}) })
fun getOptions(id: String): PluginOptions = if (id.isEmpty()) PluginOptions() else { fun getOptions(
pluginsOptions[id] ?: PluginOptions(id, PluginManager.fetchPlugins().lookup[id]?.defaultConfig) id: String = selected,
} defaultConfig: () -> String? = { PluginManager.fetchPlugins().lookup[id]?.defaultConfig }
val selectedOptions: PluginOptions get() = getOptions(selected) ) = if (id.isEmpty()) PluginOptions() else pluginsOptions[id] ?: PluginOptions(id, defaultConfig())
override fun toString(): String { override fun toString(): String {
val result = LinkedList<PluginOptions>() val result = LinkedList<PluginOptions>()
for ((id, opt) in pluginsOptions) if (id == this.selected) result.addFirst(opt) else result.addLast(opt) for ((id, opt) in pluginsOptions) if (id == this.selected) result.addFirst(opt) else result.addLast(opt)
if (!pluginsOptions.contains(selected)) result.addFirst(selectedOptions) if (!pluginsOptions.contains(selected)) result.addFirst(getOptions())
return result.joinToString("\n") { it.toString(false) } return result.joinToString("\n") { it.toString(false) }
} }
} }
...@@ -24,13 +24,12 @@ import android.annotation.SuppressLint ...@@ -24,13 +24,12 @@ import android.annotation.SuppressLint
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.ContentResolver import android.content.ContentResolver
import android.content.Intent import android.content.Intent
import android.content.pm.ComponentInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.ProviderInfo import android.content.pm.ProviderInfo
import android.content.pm.Signature import android.content.pm.Signature
import android.content.res.Resources
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.os.Bundle
import android.system.Os import android.system.Os
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
...@@ -123,30 +122,31 @@ object PluginManager { ...@@ -123,30 +122,31 @@ object PluginManager {
// the following parts are meant to be used by :bg // the following parts are meant to be used by :bg
@Throws(Throwable::class) @Throws(Throwable::class)
fun init(options: PluginOptions): String? { fun init(configuration: PluginConfiguration): Pair<String, PluginOptions>? {
if (options.id.isEmpty()) return null if (configuration.selected.isEmpty()) return null
var throwable: Throwable? = null var throwable: Throwable? = null
try { try {
val path = initNative(options) val result = initNative(configuration)
if (path != null) return path if (result != null) return result
} catch (t: Throwable) { } catch (t: Throwable) {
if (throwable == null) throwable = t else printLog(t) if (throwable == null) throwable = t else printLog(t)
} }
// add other plugin types here // add other plugin types here
throw throwable ?: PluginNotFoundException(options.id) throw throwable ?: PluginNotFoundException(configuration.selected)
} }
private fun initNative(options: PluginOptions): String? { private fun initNative(configuration: PluginConfiguration): Pair<String, PluginOptions>? {
val providers = app.packageManager.queryIntentContentProviders( val providers = app.packageManager.queryIntentContentProviders(Intent(PluginContract.ACTION_NATIVE_PLUGIN,
Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(options.id)), PackageManager.GET_META_DATA) buildUri(configuration.selected)), PackageManager.GET_META_DATA)
if (providers.isEmpty()) return null if (providers.isEmpty()) return null
val provider = providers.single().providerInfo val provider = providers.single().providerInfo
val options = configuration.getOptions { provider.loadString(PluginContract.METADATA_KEY_DEFAULT_CONFIG) }
var failure: Throwable? = null var failure: Throwable? = null
try { try {
initNativeFaster(provider)?.also { return it } initNativeFaster(provider)?.also { return it to options }
} catch (t: Throwable) { } catch (t: Throwable) {
Crashlytics.log(Log.WARN, TAG, "Initializing native plugin faster mode failed") Crashlytics.log(Log.WARN, TAG, "Initializing native plugin faster mode failed")
failure = t failure = t
...@@ -156,9 +156,8 @@ object PluginManager { ...@@ -156,9 +156,8 @@ object PluginManager {
scheme(ContentResolver.SCHEME_CONTENT) scheme(ContentResolver.SCHEME_CONTENT)
authority(provider.authority) authority(provider.authority)
}.build() }.build()
val cr = app.contentResolver
try { try {
return initNativeFast(cr, options, uri) return initNativeFast(app.contentResolver, options, uri)?.let { it to options }
} catch (t: Throwable) { } catch (t: Throwable) {
Crashlytics.log(Log.WARN, TAG, "Initializing native plugin fast mode failed") Crashlytics.log(Log.WARN, TAG, "Initializing native plugin fast mode failed")
failure?.also { t.addSuppressed(it) } failure?.also { t.addSuppressed(it) }
...@@ -166,7 +165,7 @@ object PluginManager { ...@@ -166,7 +165,7 @@ object PluginManager {
} }
try { try {
return initNativeSlow(cr, options, uri) return initNativeSlow(app.contentResolver, options, uri)?.let { it to options }
} catch (t: Throwable) { } catch (t: Throwable) {
failure?.also { t.addSuppressed(it) } failure?.also { t.addSuppressed(it) }
throw t throw t
...@@ -174,9 +173,7 @@ object PluginManager { ...@@ -174,9 +173,7 @@ object PluginManager {
} }
private fun initNativeFaster(provider: ProviderInfo): String? { private fun initNativeFaster(provider: ProviderInfo): String? {
return provider.metaData.loadString(PluginContract.METADATA_KEY_EXECUTABLE_PATH) { return provider.loadString(PluginContract.METADATA_KEY_EXECUTABLE_PATH)?.let { relativePath ->
app.packageManager.getResourcesForApplication(provider.applicationInfo)
}?.let { relativePath ->
File(provider.applicationInfo.nativeLibraryDir).resolve(relativePath).apply { File(provider.applicationInfo.nativeLibraryDir).resolve(relativePath).apply {
check(canExecute()) check(canExecute())
}.absolutePath }.absolutePath
...@@ -220,9 +217,9 @@ object PluginManager { ...@@ -220,9 +217,9 @@ object PluginManager {
return File(pluginDir, options.id).absolutePath return File(pluginDir, options.id).absolutePath
} }
fun Bundle.loadString(key: String, resources: () -> Resources) = when (val value = get(key)) { fun ComponentInfo.loadString(key: String) = when (val value = metaData.get(key)) {
is String -> value is String -> value
is Int -> resources().getString(value) is Int -> app.packageManager.getResourcesForApplication(applicationInfo).getString(value)
null -> null null -> null
else -> error("meta-data $key has invalid type ${value.javaClass}") else -> error("meta-data $key has invalid type ${value.javaClass}")
} }
......
...@@ -30,15 +30,16 @@ import com.github.shadowsocks.utils.signaturesCompat ...@@ -30,15 +30,16 @@ import com.github.shadowsocks.utils.signaturesCompat
abstract class ResolvedPlugin(protected val resolveInfo: ResolveInfo) : Plugin() { abstract class ResolvedPlugin(protected val resolveInfo: ResolveInfo) : Plugin() {
protected abstract val componentInfo: ComponentInfo protected abstract val componentInfo: ComponentInfo
private val resources by lazy { app.packageManager.getResourcesForApplication(componentInfo.applicationInfo) }
override val id by lazy { componentInfo.metaData.loadString(PluginContract.METADATA_KEY_ID) { resources }!! } override val id by lazy { componentInfo.loadString(PluginContract.METADATA_KEY_ID)!! }
override val idAliases: Array<String> by lazy { override val idAliases: Array<String> by lazy {
when (val value = componentInfo.metaData.get(PluginContract.METADATA_KEY_ID_ALIASES)) { when (val value = componentInfo.metaData.get(PluginContract.METADATA_KEY_ID_ALIASES)) {
is String -> arrayOf(value) is String -> arrayOf(value)
is Int -> when (resources.getResourceTypeName(value)) { is Int -> app.packageManager.getResourcesForApplication(componentInfo.applicationInfo).run {
"string" -> arrayOf(resources.getString(value)) when (getResourceTypeName(value)) {
else -> resources.getStringArray(value) "string" -> arrayOf(getString(value))
else -> getStringArray(value)
}
} }
null -> emptyArray() null -> emptyArray()
else -> error("unknown type for plugin meta-data idAliases") else -> error("unknown type for plugin meta-data idAliases")
...@@ -46,9 +47,7 @@ abstract class ResolvedPlugin(protected val resolveInfo: ResolveInfo) : Plugin() ...@@ -46,9 +47,7 @@ abstract class ResolvedPlugin(protected val resolveInfo: ResolveInfo) : Plugin()
} }
override val label: CharSequence get() = resolveInfo.loadLabel(app.packageManager) override val label: CharSequence get() = resolveInfo.loadLabel(app.packageManager)
override val icon: Drawable get() = resolveInfo.loadIcon(app.packageManager) override val icon: Drawable get() = resolveInfo.loadIcon(app.packageManager)
override val defaultConfig by lazy { override val defaultConfig by lazy { componentInfo.loadString(PluginContract.METADATA_KEY_DEFAULT_CONFIG) }
componentInfo.metaData.loadString(PluginContract.METADATA_KEY_DEFAULT_CONFIG) { resources }
}
override val packageName: String get() = componentInfo.packageName override val packageName: String get() = componentInfo.packageName
override val trusted by lazy { override val trusted by lazy {
Core.getPackageInfo(packageName).signaturesCompat.any(PluginManager.trustedSignatures::contains) Core.getPackageInfo(packageName).signaturesCompat.any(PluginManager.trustedSignatures::contains)
......
...@@ -132,7 +132,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -132,7 +132,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
plugin.value = pluginConfiguration.selected plugin.value = pluginConfiguration.selected
plugin.init() plugin.init()
pluginConfigure.isEnabled = pluginConfiguration.selected.isNotEmpty() pluginConfigure.isEnabled = pluginConfiguration.selected.isNotEmpty()
pluginConfigure.text = pluginConfiguration.selectedOptions.toString() pluginConfigure.text = pluginConfiguration.getOptions().toString()
} }
private fun showPluginEditor() { private fun showPluginEditor() {
...@@ -196,7 +196,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -196,7 +196,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
PluginContract.ACTION_CONFIGURE) PluginContract.ACTION_CONFIGURE)
if (intent.resolveActivity(requireContext().packageManager) == null) showPluginEditor() else { if (intent.resolveActivity(requireContext().packageManager) == null) showPluginEditor() else {
startActivityForResult(intent startActivityForResult(intent
.putExtra(PluginContract.EXTRA_OPTIONS, pluginConfiguration.selectedOptions.toString()), .putExtra(PluginContract.EXTRA_OPTIONS, pluginConfiguration.getOptions().toString()),
REQUEST_CODE_PLUGIN_CONFIGURE) REQUEST_CODE_PLUGIN_CONFIGURE)
} }
} }
...@@ -216,7 +216,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -216,7 +216,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
DataStore.plugin = pluginConfiguration.toString() DataStore.plugin = pluginConfiguration.toString()
DataStore.dirty = true DataStore.dirty = true
pluginConfigure.isEnabled = selected !is NoPlugin pluginConfigure.isEnabled = selected !is NoPlugin
pluginConfigure.text = pluginConfiguration.selectedOptions.toString() pluginConfigure.text = pluginConfiguration.getOptions().toString()
if (!selected.trusted) Snackbar.make(view!!, R.string.plugin_untrusted, Snackbar.LENGTH_LONG).show() if (!selected.trusted) Snackbar.make(view!!, R.string.plugin_untrusted, Snackbar.LENGTH_LONG).show()
} }
REQUEST_CODE_PLUGIN_CONFIGURE -> when (resultCode) { REQUEST_CODE_PLUGIN_CONFIGURE -> when (resultCode) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment