Unverified Commit e6b511f2 authored by Mygod's avatar Mygod Committed by GitHub

Merge pull request #2245 from shadowsocks/plugin-1.3.0

Plugin lib 1.3.0
parents 32e7d8d2 34387bb1
...@@ -5,7 +5,7 @@ apply plugin: 'com.github.ben-manes.versions' ...@@ -5,7 +5,7 @@ apply plugin: 'com.github.ben-manes.versions'
buildscript { buildscript {
ext { ext {
javaVersion = JavaVersion.VERSION_1_8 javaVersion = JavaVersion.VERSION_1_8
kotlinVersion = '1.3.31' kotlinVersion = '1.3.40'
minSdkVersion = 21 minSdkVersion = 21
sdkVersion = 29 sdkVersion = 29
compileSdkVersion = 29 compileSdkVersion = 29
......
...@@ -59,8 +59,8 @@ dependencies { ...@@ -59,8 +59,8 @@ dependencies {
api 'androidx.work:work-runtime-ktx:2.1.0-beta01' api 'androidx.work:work-runtime-ktx:2.1.0-beta01'
api 'com.crashlytics.sdk.android:crashlytics:2.10.1' api 'com.crashlytics.sdk.android:crashlytics:2.10.1'
api 'com.google.code.gson:gson:2.8.5' api 'com.google.code.gson:gson:2.8.5'
api 'com.google.firebase:firebase-config:17.0.0' api 'com.google.firebase:firebase-config:18.0.0'
api 'com.google.firebase:firebase-core:16.0.9' api 'com.google.firebase:firebase-core:17.0.0'
api 'dnsjava:dnsjava:2.1.9' api 'dnsjava:dnsjava:2.1.9'
api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1' api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
api 'org.connectbot.jsocks:jsocks:1.0.0' api 'org.connectbot.jsocks:jsocks:1.0.0'
......
...@@ -25,6 +25,7 @@ import android.content.BroadcastReceiver ...@@ -25,6 +25,7 @@ import android.content.BroadcastReceiver
import android.content.ContentResolver import android.content.ContentResolver
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.ProviderInfo
import android.content.pm.Signature import android.content.pm.Signature
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
...@@ -41,6 +42,8 @@ import java.io.File ...@@ -41,6 +42,8 @@ import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
object PluginManager { object PluginManager {
private const val TAG = "PluginManager"
class PluginNotFoundException(private val plugin: String) : FileNotFoundException(plugin) { class PluginNotFoundException(private val plugin: String) : FileNotFoundException(plugin) {
override fun getLocalizedMessage() = app.getString(com.github.shadowsocks.core.R.string.plugin_unknown, plugin) override fun getLocalizedMessage() = app.getString(com.github.shadowsocks.core.R.string.plugin_unknown, plugin)
} }
...@@ -137,28 +140,51 @@ object PluginManager { ...@@ -137,28 +140,51 @@ object PluginManager {
private fun initNative(options: PluginOptions): String? { private fun initNative(options: PluginOptions): String? {
val providers = app.packageManager.queryIntentContentProviders( 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 if (providers.isEmpty()) return null
val uri = Uri.Builder() val provider = providers.single().providerInfo
.scheme(ContentResolver.SCHEME_CONTENT) var failure: Throwable? = null
.authority(providers.single().providerInfo.authority) try {
.build() 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 val cr = app.contentResolver
return try { try {
initNativeFast(cr, options, uri) 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) { } catch (t: Throwable) {
Crashlytics.log(Log.WARN, "PluginManager", failure?.also { t.addSuppressed(it) }
"Initializing native plugin fast mode failed. Falling back to slow mode.") throw t
printLog(t)
initNativeSlow(cr, options, uri)
} }
} }
private fun initNativeFast(cr: ContentResolver, options: PluginOptions, uri: Uri): String { private fun initNativeFaster(provider: ProviderInfo): String? {
val result = cr.call(uri, PluginContract.METHOD_GET_EXECUTABLE, null, return provider.metaData.getString(PluginContract.METADATA_KEY_EXECUTABLE_PATH)?.let { relativePath ->
bundleOf(Pair(PluginContract.EXTRA_OPTIONS, options.id)))!!.getString(PluginContract.EXTRA_ENTRY)!! File(provider.applicationInfo.nativeLibraryDir).resolve(relativePath).apply {
check(File(result).canExecute()) check(canExecute())
return result }.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") @SuppressLint("Recycle")
......
...@@ -60,9 +60,9 @@ androidExtensions { ...@@ -60,9 +60,9 @@ androidExtensions {
dependencies { dependencies {
implementation project(':core') implementation project(':core')
implementation 'androidx.browser:browser:1.0.0' implementation 'androidx.browser:browser:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
implementation 'com.google.android.gms:play-services-vision:17.0.2' implementation 'com.google.android.gms:play-services-vision:18.0.0'
implementation 'com.google.firebase:firebase-ads:17.2.1' implementation 'com.google.firebase:firebase-ads:18.0.0'
implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.0.0' implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.0.0'
implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.4' implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.4'
implementation 'net.glxn.qrgen:android:2.0' implementation 'net.glxn.qrgen:android:2.0'
......
* 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:
- Now targeting API 29;
- `androidx.core:core-ktx:1.1.0-rc01`;
- `com.google.android.material:material:1.1.0-alpha07`;
- `org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.40`.
* 1.2.0: * 1.2.0:
* New helper class `AlertDialogFragment` for creating `AlertDialog` that persists through configuration changes; * New helper class `AlertDialogFragment` for creating `AlertDialog` that persists through configuration changes;
* Dependency update: `com.google.android.material:material:1.1.0-alpha03`. * Dependency update: `com.google.android.material:material:1.1.0-alpha03`.
......
...@@ -136,8 +136,8 @@ Every native mode plugin MUST have a content provider to provide the native exec ...@@ -136,8 +136,8 @@ Every native mode plugin MUST have a content provider to provide the native exec
* MUST implement `query` that returns the file list which MUST include `$PLUGIN_ID` when having * MUST implement `query` that returns the file list which MUST include `$PLUGIN_ID` when having
these as arguments: these as arguments:
- `uri = "content://$authority_of_your_provider`; - `uri = "content://$authority_of_your_provider`;
- `projection = ["path", "mode"]`; (relative path, for example `obfs-local`; file mode, for - `projection = ["path", "mode"]`; (relative path, for example `obfs-local`; file mode as integer, for
example `755`) example `0b110100100`)
- `selection = null`; - `selection = null`;
- `selectionArgs = null`; - `selectionArgs = null`;
- `sortOrder = null`; - `sortOrder = null`;
...@@ -186,10 +186,19 @@ If your plugin binary executable can run in place, you can support native mode w ...@@ -186,10 +186,19 @@ If your plugin binary executable can run in place, you can support native mode w
`com.github.shadowsocks.plugin.EXTRA_ENTRY` when having `method = "shadowsocks:getExecutable"`; `com.github.shadowsocks.plugin.EXTRA_ENTRY` when having `method = "shadowsocks:getExecutable"`;
(`com.github.shadowsocks.plugin.EXTRA_OPTIONS` is provided in extras as well just in case you (`com.github.shadowsocks.plugin.EXTRA_OPTIONS` is provided in extras as well just in case you
need them) need them)
* SHOULD define `android:installLocation="internalOnly"` for `<manifest>` in AndroidManifest.xml;
* SHOULD define `android:extractNativeLibs="true"` for `<application>` in AndroidManifest.xml;
If you don't plan to support this mode, you can just throw `UnsupportedOperationException` when If you don't plan to support this mode, you can just throw `UnsupportedOperationException` when
being invoked. It will fallback to the slow routine automatically. being invoked. It will fallback to the slow routine automatically.
### Native mode without binary copying and setup
Additionally, if your plugin only needs to supply the path of your executable without doing any extra setup work,
you can use an additional `meta-data` with name `com.github.shadowsocks.plugin.executable_path`
to supply executable path to your native binary.
This allows the host app to launch your plugin without ever launching your app.
## JVM mode ## JVM mode
This feature hasn't been implemented yet. This feature hasn't been implemented yet.
...@@ -229,7 +238,7 @@ Plugin app must include this in their application tag: (which should be automati ...@@ -229,7 +238,7 @@ Plugin app must include this in their application tag: (which should be automati
``` ```
<meta-data android:name="com.github.shadowsocks.plugin.version" <meta-data android:name="com.github.shadowsocks.plugin.version"
android:value="0.0.2"/> android:value="1.0.0"/>
``` ```
# Android TV # Android TV
......
GROUP=com.github.shadowsocks GROUP=com.github.shadowsocks
VERSION_NAME=1.2.0 VERSION_NAME=1.3.0
VERSION_CODE=9 VERSION_CODE=10
POM_ARTIFACT_ID=plugin POM_ARTIFACT_ID=plugin
POM_NAME=Shadowsocks Plugin POM_NAME=Shadowsocks Plugin
......
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
<application <application
android:theme="@style/Theme.Shadowsocks"> android:theme="@style/Theme.Shadowsocks">
<meta-data android:name="com.github.shadowsocks.plugin.version" <meta-data android:name="com.github.shadowsocks.plugin.version"
android:value="0.1.0"/> android:value="1.3.0"/>
</application> </application>
</manifest> </manifest>
...@@ -69,7 +69,11 @@ abstract class NativePluginProvider : ContentProvider() { ...@@ -69,7 +69,11 @@ abstract class NativePluginProvider : ContentProvider() {
} }
/** /**
* Returns executable entry absolute path. This is used if plugin is sharing UID with the host. * Returns executable entry absolute path.
* This is used for fast mode initialization where ss-local launches your native binary at the path given directly.
* In order for this to work, plugin app is encouraged to have the following in its AndroidManifest.xml:
* - android:installLocation="internalOnly" for <manifest>
* - android:extractNativeLibs="true" for <application>
* *
* Default behavior is throwing UnsupportedOperationException. If you don't wish to use this feature, use the * Default behavior is throwing UnsupportedOperationException. If you don't wish to use this feature, use the
* default behavior. * default behavior.
......
...@@ -84,6 +84,23 @@ object PluginContract { ...@@ -84,6 +84,23 @@ object PluginContract {
* Constant Value: "com.github.shadowsocks.plugin.default_config" * Constant Value: "com.github.shadowsocks.plugin.default_config"
*/ */
const val METADATA_KEY_DEFAULT_CONFIG = "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 (aka faster mode).
* In order for this to work, plugin app is encouraged to have the following in its AndroidManifest.xml:
* - android:installLocation="internalOnly" for <manifest>
* - android:extractNativeLibs="true" for <application>
*
* 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" const val METHOD_GET_EXECUTABLE = "shadowsocks:getExecutable"
...@@ -99,11 +116,11 @@ object PluginContract { ...@@ -99,11 +116,11 @@ object PluginContract {
*/ */
const val COLUMN_PATH = "path" const val COLUMN_PATH = "path"
/** /**
* File mode bits. Default value is "644". * File mode bits. Default value is 644 in octal.
* *
* Example: "755" * Example: 0b110100100 (for 755 in octal)
* *
* Type: String * Type: Int or String (deprecated)
*/ */
const val COLUMN_MODE = "mode" const val COLUMN_MODE = "mode"
......
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