Xposed模块内部绑定BridgeService失败问题求助
我正在开发一款Android Xposed模块,需要实现带GUI的钩子开关管理器,核心是在handleLoadPackage()(实现IXposedHookLoadPackage)初始化的Xposed部分读写配置。
查过文档和论坛后,确定桥接服务是最优方案,还参考了同类Xposed模块的服务/客户端/桥接逻辑,但现在遇到瓶颈:模块无法绑定桥接服务,Xposed部分找不到BridgeService,Logcat仅输出以下错误:
W ActivityManager: Unable to start service Intent { cmp=com.friction/.bridge.BridgeService } U=0: not found
问题出在bindService()调用上——按传入的标志,该方法应创建并绑定服务,但执行失败,传递的Intent不被识别为有效,尽管所有必要组件都已配置。
针对同类问题,有人建议在manifest的<queries>中添加服务所在包名,但没用;而且我认为这一步没必要,因为客户端和服务在同一包内。我也试过在manifest里写全路径android:name="com.friction.bridge.BridgeService",同样无效。
这个问题卡了好几周毫无头绪,但参考的同类模块用相同方法却能正常运行。
服务定义(BridgeService.kt)
package com.friction.bridge import android.app.Service import android.content.Intent import android.os.IBinder import com.friction.bridge.types.ActionType import com.friction.bridge.types.FileType import com.friction.xposed.context.RemoteSideContext import com.friction.xposed.context.SharedContextHolder class BridgeService : Service() { private lateinit var remoteSideContext: RemoteSideContext override fun onDestroy() { if (::remoteSideContext.isInitialized) { remoteSideContext.bridgeService = null } } override fun onBind(intent: Intent): IBinder? { remoteSideContext = SharedContextHolder.remote(this) remoteSideContext.apply { bridgeService = this@BridgeService } return BridgeBinder() } inner class BridgeBinder : BridgeInterface.Stub() { override fun fileOperation(action: Int, fileType: Int, content: ByteArray?): ByteArray { val resolvedFile = FileType.fromValue(fileType)?.resolve(this@BridgeService) return when (ActionType.entries[action]) { ActionType.CREATE_AND_READ -> { resolvedFile?.let { if (!it.exists()) { return content?.also { content -> it.writeBytes(content) } ?: ByteArray( 0 ) } it.readBytes() } ?: ByteArray(0) } ActionType.READ -> { resolvedFile?.takeIf { it.exists() }?.readBytes() ?: ByteArray(0) } ActionType.WRITE -> { content?.also { resolvedFile?.writeBytes(content) } ?: ByteArray(0) } ActionType.DELETE -> { resolvedFile?.takeIf { it.exists() }?.delete() ByteArray(0) } ActionType.EXISTS -> { if (resolvedFile?.exists() == true) ByteArray(1) else ByteArray(0) } } } override fun registerConfigStateListener(listener: ConfigStateListener?) { remoteSideContext.config.configStateListener = listener } } }
客户端定义(BridgeClient.kt)
package com.friction.bridge import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.os.Build import android.os.Handler import android.os.HandlerThread import android.os.IBinder import android.util.Log import com.friction.BuildConfig import com.friction.bridge.types.ActionType import com.friction.bridge.types.FileType import com.friction.core.Constants.LOG_TAG import com.friction.xposed.context.ModContext import de.robv.android.xposed.XposedHelpers import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import kotlin.system.exitProcess fun FileLoaderWrapper.loadFromBridge(bridgeClient: BridgeClient) { isFileExists = { bridgeClient.isFileExists(fileType) } read = { bridgeClient.createAndReadFile(fileType, defaultData) } write = { bridgeClient.writeFile(fileType, it) } delete = { bridgeClient.deleteFile(fileType) } } class BridgeClient(private val context: ModContext): ServiceConnection { private lateinit var future: CompletableFuture<Boolean> private lateinit var service: BridgeInterface fun connect(onFailure: (Throwable) -> Unit, onResult: (Boolean) -> Unit) { this.future = CompletableFuture() with(context.androidContext) { startActivity(Intent() .setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".bridge.ForceStartActivity") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) ) val intent = Intent() .setClassName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".bridge.BridgeService") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (!bindService( intent, Context.BIND_AUTO_CREATE, Executors.newSingleThreadExecutor(), this@BridgeClient )) { onFailure(IllegalStateException("Cannot bind to bridge service")) } else { onResult(true) } } else { XposedHelpers.callMethod( this, "bindServiceAsUser", intent, this@BridgeClient, Context.BIND_AUTO_CREATE, Handler(HandlerThread("BridgeClient").apply { start() }.looper), android.os.Process.myUserHandle() ) } } runCatching { onResult(future.get(15, TimeUnit.SECONDS)) }.onFailure { onFailure(it) } } override fun onServiceConnected(name: ComponentName?, service: IBinder?) { this.service = BridgeInterface.Stub.asInterface(service) future.complete(true) } override fun onNullBinding(name: ComponentName?) { Log.i("BridgeClient", "cannot connect to bridge service") exitProcess(1) } override fun onServiceDisconnected(name: ComponentName?) { Log.i("BridgeClient", "service disconnected") exitProcess(1) } fun createAndReadFile(type: FileType, defaultData: ByteArray): ByteArray { return service.fileOperation(ActionType.CREATE_AND_READ.ordinal, type.value, defaultData) } fun writeFile(type: FileType, data: ByteArray) { service.fileOperation(ActionType.WRITE.ordinal, type.value, data) } fun readFile(type: FileType): ByteArray { return service.fileOperation(ActionType.READ.ordinal, type.value, null) } fun deleteFile(type: FileType) { service.fileOperation(ActionType.DELETE.ordinal, type.value, null) } fun isFileExists(type: FileType): Boolean { return service.fileOperation(ActionType.EXISTS.ordinal, type.value, null).isNotEmpty() } fun registerConfigStateListener(listener: ConfigStateListener) { service.registerConfigStateListener(listener) } }
Manifest中的服务声明(AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Friction" tools:targetApi="34"> <meta-data android:name="xposedmodule" android:value="true" /> <meta-data android:name="xposeddescription" android:value="@string/module_description" /> <meta-data android:name="xposedminversion" android:value="93" /> <service android:name=".bridge.BridgeService" android:exported="true" tools:ignore="ExportedService"> </service> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".bridge.ForceStartActivity" android:theme="@android:style/Theme.NoDisplay" android:excludeFromRecents="true" android:exported="true" /> </application> </manifest>
补充信息
在应用内部尝试绑定服务时失败,但用独立的外部测试应用却能成功绑定同一服务——问题仅出在模块内部绑定,外部应用完全正常。测试代码如下:
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) runCatching { val intent = Intent() .setClassName("com.friction", "com.friction.bridge.BridgeService") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val serviceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { Log.i("BridgeClient", "Service connected") } override fun onServiceDisconnected(name: ComponentName?) { Log.i("BridgeClient", "Service disconnected") } fun connect() { val ret = bindService( intent, Context.BIND_AUTO_CREATE, Executors.newSingleThreadExecutor(), this ) Log.i("BridgeClient", "bindService returned $ret") } } serviceConnection.connect() } } } }
需要更多信息可以补充,求帮忙解决。
内容的提问来源于stack exchange,提问作者Rogelio

