Commit 1247c683 authored by Mygod's avatar Mygod

Introduce faster native plugin initialization

Please read doc for METADATA_KEY_EXECUTABLE_PATH for more details.
Basically, we get around application launching limitations from Huawei (among others) by reading metadata and therefore prevent launching the application altogether.
This should hopefully fix #1091, #1106. (requires plugin app support)

This commit also refines error reporting in PluginManager.
parent ccb68ac7
......@@ -25,6 +25,7 @@ import android.content.BroadcastReceiver
import android.content.ContentResolver
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ProviderInfo
import android.content.pm.Signature
import android.database.Cursor
import android.net.Uri
......@@ -41,6 +42,8 @@ import java.io.File
import java.io.FileNotFoundException
object PluginManager {
private const val TAG = "PluginManager"
class PluginNotFoundException(private val plugin: String) : FileNotFoundException(plugin) {
override fun getLocalizedMessage() = app.getString(com.github.shadowsocks.core.R.string.plugin_unknown, plugin)
}
......@@ -137,28 +140,51 @@ object PluginManager {
private fun initNative(options: PluginOptions): String? {
val providers = app.packageManager.queryIntentContentProviders(
Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(options.id)), 0)
Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(options.id)), PackageManager.GET_META_DATA)
if (providers.isEmpty()) return null
val uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(providers.single().providerInfo.authority)
.build()
val provider = providers.single().providerInfo
var failure: Throwable? = null
try {
initNativeFaster(provider)?.also { return it }
} catch (t: Throwable) {
Crashlytics.log(Log.WARN, TAG, "Initializing native plugin faster mode failed")
failure = t
}
val uri = Uri.Builder().apply {
scheme(ContentResolver.SCHEME_CONTENT)
authority(provider.authority)
}.build()
val cr = app.contentResolver
return try {
initNativeFast(cr, options, uri)
try {
return initNativeFast(cr, options, uri)
} catch (t: Throwable) {
Crashlytics.log(Log.WARN, TAG, "Initializing native plugin fast mode failed")
failure?.also { t.addSuppressed(it) }
failure = t
}
try {
return initNativeSlow(cr, options, uri)
} catch (t: Throwable) {
Crashlytics.log(Log.WARN, "PluginManager",
"Initializing native plugin fast mode failed. Falling back to slow mode.")
printLog(t)
initNativeSlow(cr, options, uri)
failure?.also { t.addSuppressed(it) }
throw t
}
}
private fun initNativeFast(cr: ContentResolver, options: PluginOptions, uri: Uri): String {
val result = cr.call(uri, PluginContract.METHOD_GET_EXECUTABLE, null,
bundleOf(Pair(PluginContract.EXTRA_OPTIONS, options.id)))!!.getString(PluginContract.EXTRA_ENTRY)!!
check(File(result).canExecute())
return result
private fun initNativeFaster(provider: ProviderInfo): String? {
return provider.metaData.getString(PluginContract.METADATA_KEY_EXECUTABLE_PATH)?.let { relativePath ->
File(provider.applicationInfo.nativeLibraryDir).resolve(relativePath).apply {
check(canExecute())
}.absolutePath
}
}
private fun initNativeFast(cr: ContentResolver, options: PluginOptions, uri: Uri): String? {
return cr.call(uri, PluginContract.METHOD_GET_EXECUTABLE, null,
bundleOf(PluginContract.EXTRA_OPTIONS to options.id))?.getString(PluginContract.EXTRA_ENTRY)?.also {
check(File(it).canExecute())
}
}
@SuppressLint("Recycle")
......
* 1.3.0:
* Optional new metadata `com.github.shadowsocks.plugin.executable_path` for even faster initialization;
(see doc for `PluginContract.METADATA_KEY_EXECUTABLE_PATH` for more information)
* Breaking API change: `val AlertDialogFragment.ret: Ret?` => `fun AlertDialogFragment.ret(which: Int): Ret?`;
(nothing needs to be done if you are not using this API)
* Dependency updates:
......
......@@ -84,6 +84,19 @@ object PluginContract {
* Constant Value: "com.github.shadowsocks.plugin.default_config"
*/
const val METADATA_KEY_DEFAULT_CONFIG = "com.github.shadowsocks.plugin.default_config"
/**
* The metadata key to retrieve executable path to your native binary.
* This path should be relative to your application's nativeLibraryDir.
*
* If this is set, the host app will prefer this value and (probably) not launch your app at all.
* Do not use this if you plan to do some setup work before giving away your binary path,
* or your native binary is not at a fixed location relative to your application's nativeLibraryDir.
*
* Since plugin lib: 1.3.0
*
* Constant Value: "com.github.shadowsocks.plugin.executable_path"
*/
const val METADATA_KEY_EXECUTABLE_PATH = "com.github.shadowsocks.plugin.executable_path"
const val METHOD_GET_EXECUTABLE = "shadowsocks:getExecutable"
......
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