From e0059114c997f9437ed280500747c1bab23063a9 Mon Sep 17 00:00:00 2001 From: Leaf <1587968048@qq.com> Date: Mon, 13 Jul 2026 21:46:37 +0800 Subject: [PATCH 1/4] Fix AbstractMethodError crash on Android 17 IServiceConnection Android 17 (API 36) replaced the old 3-argument IServiceConnection.connected(ComponentName, IBinder, boolean) with a new 4-argument overload that adds an IBinderSession parameter, and removed the old overload entirely from the framework interface. Vector's IServiceConnection stub and the ManagerService connection implementation only declared/overrode the old 3-argument signature. When system_server dispatched the new 4-argument connected() to the parasitic manager process on Android 17, the Stub lacked the new abstract method and threw AbstractMethodError, killing the manager process. system_server then hit DeadObjectException while dispatching a broadcast to the dead process, triggering a framework restart (SYSTEM_RESTART) every time the manager was opened. Fix: - Add an IBinderSession stub (android.app.IBinderSession extends IInterface) with binderTransactionCompleted(long) and binderTransactionStarting(String):long, matching the Android 17 framework interface. - Declare both connected() overloads in the IServiceConnection stub so it compiles against Android 8.1~17 (the old overload is simply never invoked on Android 17+). - Override the new 4-argument connected() in ManagerService's connection Stub (no-op, same as the existing 3-argument override). --- .../vector/daemon/ipc/ManagerService.kt | 9 +++++ .../main/java/android/app/IBinderSession.java | 35 +++++++++++++++++++ .../java/android/app/IServiceConnection.java | 14 ++++++++ 3 files changed, 58 insertions(+) create mode 100644 hiddenapi/stubs/src/main/java/android/app/IBinderSession.java diff --git a/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt b/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt index 412165747..28f0cb50a 100644 --- a/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt +++ b/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt @@ -54,7 +54,16 @@ object ManagerService : ILSPManagerService.Stub() { IBinder.DeathRecipient { private val connection = object : android.app.IServiceConnection.Stub() { + // Android 8.1 ~ 16 override fun connected(name: ComponentName?, service: IBinder?, dead: Boolean) {} + + // Android 17+ (new signature with IBinderSession) + override fun connected( + name: ComponentName?, + service: IBinder?, + session: android.app.IBinderSession?, + dead: Boolean + ) {} } init { diff --git a/hiddenapi/stubs/src/main/java/android/app/IBinderSession.java b/hiddenapi/stubs/src/main/java/android/app/IBinderSession.java new file mode 100644 index 000000000..1dc3d5777 --- /dev/null +++ b/hiddenapi/stubs/src/main/java/android/app/IBinderSession.java @@ -0,0 +1,35 @@ +package android.app; + +import android.os.IBinder; +import android.os.IInterface; + +/** + * Stub of {@code android.app.IBinderSession} introduced in Android 17 (API 36+). + * + *

This interface is used as a parameter of the new + * {@link IServiceConnection#connected} overload added in Android 17. On older + * Android versions this class does not exist at runtime, but because the stub + * is only referenced from the new {@code connected} overload (which is never + * invoked on older versions), the absence of the runtime class is not a + * problem — the method is never dispatched there. + */ +public interface IBinderSession extends IInterface { + + String DESCRIPTOR = "android.app.IBinderSession"; + + void binderTransactionCompleted(long transactionId); + + long binderTransactionStarting(String name); + + abstract class Stub extends android.os.Binder implements IBinderSession { + + public static IBinderSession asInterface(IBinder obj) { + throw new UnsupportedOperationException(); + } + + @Override + public IBinder asBinder() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java b/hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java index a531e1bc1..4bdcf7637 100644 --- a/hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java +++ b/hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java @@ -6,8 +6,22 @@ import android.os.IInterface; public interface IServiceConnection extends IInterface { + + /** + * Old signature, used on Android 8.1 ~ 16. Removed from the framework + * interface in Android 17, but kept here so that code compiling against + * this stub can still override it (the method is simply never invoked on + * Android 17+). + */ void connected(ComponentName name, IBinder service, boolean dead); + /** + * New signature introduced in Android 17 (API 36+). On older Android + * versions this overload does not exist at runtime and is never invoked, + * so overriding it is harmless there. + */ + void connected(ComponentName name, IBinder service, IBinderSession session, boolean dead); + abstract class Stub extends Binder implements IServiceConnection { public static IServiceConnection asInterface(IBinder obj) { From 73f7b331f7e8f09433133e9e4460c3649a1ea99c Mon Sep 17 00:00:00 2001 From: Leaf <1587968048@qq.com> Date: Mon, 13 Jul 2026 21:47:04 +0800 Subject: [PATCH 2/4] Fix native version flag quoting for Windows builds The build system passed string macros to the C/C++ compiler wrapped in single quotes, e.g. -DVERSION_NAME='2.0' and -DINJECTED_PACKAGE_NAME='com.android.shell'. On Linux/macOS the shell strips the single quotes and the compiler sees a quoted string literal, but on Windows single quotes are not shell quoting characters, so the compiler receives -DVERSION_NAME='2.0' (a multi-character constant) or -DINJECTED_PACKAGE_NAME='com.android.shell', causing compilation failures: - narrowing conversion from 'int' to 'const char*' - expected unqualified-id / multi-character constant Fix by passing the values as bare tokens (-DVERSION_NAME=2.0, -DINJECTED_PACKAGE_NAME=com.android.shell) and stringizing them at the C++ side via a STRINGIZE macro, which is robust across Windows and Unix toolchains. Also move the stringize helper macros out of a namespace (macros are not namespace members and cannot be invoked with a qualified name). --- build.gradle.kts | 2 +- native/include/common/config.h | 9 ++++++++- zygisk/build.gradle.kts | 4 ++-- zygisk/src/main/cpp/module.cpp | 12 ++++++++---- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0b078cd04..08ecb2f86 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -102,7 +102,7 @@ subprojects { val flags = listOf( "-DVERSION_CODE=${versionCodeProvider.get()}", - "-DVERSION_NAME='\"${versionNameProvider.get()}\"'", + "-DVERSION_NAME=${versionNameProvider.get()}", ) val args = diff --git a/native/include/common/config.h b/native/include/common/config.h index 88beeeb63..97a06461b 100644 --- a/native/include/common/config.h +++ b/native/include/common/config.h @@ -45,7 +45,14 @@ inline constexpr auto kLinkerPath = "/linker"; /// The version code of the library, populated by the build system. const int kVersionCode = VERSION_CODE; +// Stringize the VERSION_NAME token so the build system can pass it as a bare +// token (e.g. -DVERSION_NAME=2.0) without quoting, which is fragile across +// Windows / Unix toolchains. Macros are not namespace members, so these must +// live at file scope. +#define VECTOR_STRINGIZE(x) #x +#define VECTOR_STRINGIZE_TOKEN(x) VECTOR_STRINGIZE(x) + /// The version name of the library, populated by the build system. -const char *const kVersionName = VERSION_NAME; +const char *const kVersionName = VECTOR_STRINGIZE_TOKEN(VERSION_NAME); } // namespace vector::native diff --git a/zygisk/build.gradle.kts b/zygisk/build.gradle.kts index 1757ab170..be214fe55 100644 --- a/zygisk/build.gradle.kts +++ b/zygisk/build.gradle.kts @@ -27,9 +27,9 @@ android { val flags = listOf( - "-DINJECTED_PACKAGE_NAME='\"${injectedPackageName}\"'", + "-DINJECTED_PACKAGE_NAME=${injectedPackageName}", "-DINJECTED_PACKAGE_UID=${injectedPackageUid}", - "-DMANAGER_PACKAGE_NAME='\"${defaultManagerPackageName}\"'", + "-DMANAGER_PACKAGE_NAME=${defaultManagerPackageName}", ) externalNativeBuild { diff --git a/zygisk/src/main/cpp/module.cpp b/zygisk/src/main/cpp/module.cpp index 1692239a0..8c2b010e1 100644 --- a/zygisk/src/main/cpp/module.cpp +++ b/zygisk/src/main/cpp/module.cpp @@ -32,10 +32,14 @@ constexpr int SHARED_RELRO_UID = 1037; // Android uses this to separate users. UID = AppID + UserID * 10000. constexpr int PER_USER_RANGE = 100000; -// Defined via CMake generated marcos +// Defined via CMake generated marcos. +// Package names are passed as bare tokens (no quotes) because quoting is +// fragile across Windows / Unix toolchains; stringize them here instead. +#define VECTOR_STR(x) #x +#define VECTOR_STR_TOKEN(x) VECTOR_STR(x) constexpr uid_t kHostPackageUid = INJECTED_PACKAGE_UID; -const char *const kHostPackageName = INJECTED_PACKAGE_NAME; -const char *const kManagerPackageName = MANAGER_PACKAGE_NAME; +const char *const kHostPackageName = VECTOR_STR_TOKEN(INJECTED_PACKAGE_NAME); +const char *const kManagerPackageName = VECTOR_STR_TOKEN(MANAGER_PACKAGE_NAME); constexpr uid_t GID_INET = 3003; // Android's Internet group ID. enum RuntimeFlags : uint32_t { @@ -260,7 +264,7 @@ void VectorModule::preAppSpecialize(zygisk::AppSpecializeArgs *args) { jint inet_gid = GID_INET; env_->SetIntArrayRegion(new_gids, original_gids_count, 1, &inet_gid); - args->nice_name = env_->NewStringUTF(INJECTED_PACKAGE_NAME); + args->nice_name = env_->NewStringUTF(kHostPackageName); args->gids = new_gids; } } From fc6924e5ad3c128ff1a979e7b71c514ced7d1b7a Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Sat, 25 Jul 2026 07:44:28 +0200 Subject: [PATCH 3/4] Revert "Fix native version flag quoting for Windows builds" This reverts commit 73f7b331f7e8f09433133e9e4460c3649a1ea99c. The quoting problem is unrelated to the Android 17 crash and only affects local builds on Windows; CI builds on ubuntu-latest, where the shell strips the quotes as intended. It is handled separately so that this pull request carries a single change. --- build.gradle.kts | 2 +- native/include/common/config.h | 9 +-------- zygisk/build.gradle.kts | 4 ++-- zygisk/src/main/cpp/module.cpp | 12 ++++-------- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 08ecb2f86..0b078cd04 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -102,7 +102,7 @@ subprojects { val flags = listOf( "-DVERSION_CODE=${versionCodeProvider.get()}", - "-DVERSION_NAME=${versionNameProvider.get()}", + "-DVERSION_NAME='\"${versionNameProvider.get()}\"'", ) val args = diff --git a/native/include/common/config.h b/native/include/common/config.h index 97a06461b..88beeeb63 100644 --- a/native/include/common/config.h +++ b/native/include/common/config.h @@ -45,14 +45,7 @@ inline constexpr auto kLinkerPath = "/linker"; /// The version code of the library, populated by the build system. const int kVersionCode = VERSION_CODE; -// Stringize the VERSION_NAME token so the build system can pass it as a bare -// token (e.g. -DVERSION_NAME=2.0) without quoting, which is fragile across -// Windows / Unix toolchains. Macros are not namespace members, so these must -// live at file scope. -#define VECTOR_STRINGIZE(x) #x -#define VECTOR_STRINGIZE_TOKEN(x) VECTOR_STRINGIZE(x) - /// The version name of the library, populated by the build system. -const char *const kVersionName = VECTOR_STRINGIZE_TOKEN(VERSION_NAME); +const char *const kVersionName = VERSION_NAME; } // namespace vector::native diff --git a/zygisk/build.gradle.kts b/zygisk/build.gradle.kts index be214fe55..1757ab170 100644 --- a/zygisk/build.gradle.kts +++ b/zygisk/build.gradle.kts @@ -27,9 +27,9 @@ android { val flags = listOf( - "-DINJECTED_PACKAGE_NAME=${injectedPackageName}", + "-DINJECTED_PACKAGE_NAME='\"${injectedPackageName}\"'", "-DINJECTED_PACKAGE_UID=${injectedPackageUid}", - "-DMANAGER_PACKAGE_NAME=${defaultManagerPackageName}", + "-DMANAGER_PACKAGE_NAME='\"${defaultManagerPackageName}\"'", ) externalNativeBuild { diff --git a/zygisk/src/main/cpp/module.cpp b/zygisk/src/main/cpp/module.cpp index 8c2b010e1..1692239a0 100644 --- a/zygisk/src/main/cpp/module.cpp +++ b/zygisk/src/main/cpp/module.cpp @@ -32,14 +32,10 @@ constexpr int SHARED_RELRO_UID = 1037; // Android uses this to separate users. UID = AppID + UserID * 10000. constexpr int PER_USER_RANGE = 100000; -// Defined via CMake generated marcos. -// Package names are passed as bare tokens (no quotes) because quoting is -// fragile across Windows / Unix toolchains; stringize them here instead. -#define VECTOR_STR(x) #x -#define VECTOR_STR_TOKEN(x) VECTOR_STR(x) +// Defined via CMake generated marcos constexpr uid_t kHostPackageUid = INJECTED_PACKAGE_UID; -const char *const kHostPackageName = VECTOR_STR_TOKEN(INJECTED_PACKAGE_NAME); -const char *const kManagerPackageName = VECTOR_STR_TOKEN(MANAGER_PACKAGE_NAME); +const char *const kHostPackageName = INJECTED_PACKAGE_NAME; +const char *const kManagerPackageName = MANAGER_PACKAGE_NAME; constexpr uid_t GID_INET = 3003; // Android's Internet group ID. enum RuntimeFlags : uint32_t { @@ -264,7 +260,7 @@ void VectorModule::preAppSpecialize(zygisk::AppSpecializeArgs *args) { jint inet_gid = GID_INET; env_->SetIntArrayRegion(new_gids, original_gids_count, 1, &inet_gid); - args->nice_name = env_->NewStringUTF(kHostPackageName); + args->nice_name = env_->NewStringUTF(INJECTED_PACKAGE_NAME); args->gids = new_gids; } } From e883e231dacd3bba41244958ca61763102e6f9c6 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Sat, 25 Jul 2026 07:46:02 +0200 Subject: [PATCH 4/4] Fix daemon crash from the Android 17 IServiceConnection change Android 17 (API 37) reshaped the IServiceConnection callback and dropped the old overload instead of keeping both: void connected(in ComponentName name, IBinder service, in @nullable IBinderSession session, boolean dead); ManagerGuard overrode only the three-argument form, so as soon as system_server dispatched the new transaction the Stub landed on an abstract method and the daemon died with AbstractMethodError, taking the manager session down with it. Only the Xiaomi XSpace workaround binds this connection, which is why the crash was reported on HyperOS first; the interface change itself ships in stock Android 17 and is not vendor specific. Declare both overloads in the IServiceConnection stub and override both in ManagerGuard, so every supported release finds the method system_server dispatches. IBinderSession is stubbed as an empty interface, since the type is only referenced by the descriptor of the new overload. Verified on a Pixel 6 running Android 17 (SDK 37.1), whose framework declares the four-argument overload alone: a Stub subclass overriding only the three-argument form throws AbstractMethodError on an Android 17 shaped transaction, while overriding both dispatches the four-argument method with a null session and no crash. --- .../vector/daemon/ipc/ManagerService.kt | 4 +-- .../main/java/android/app/IBinderSession.java | 32 ++----------------- .../java/android/app/IServiceConnection.java | 13 ++------ 3 files changed, 7 insertions(+), 42 deletions(-) diff --git a/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt b/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt index 28f0cb50a..94b38bf3b 100644 --- a/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt +++ b/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt @@ -52,12 +52,12 @@ object ManagerService : ILSPManagerService.Stub() { class ManagerGuard(private val binder: IBinder, val pid: Int, val uid: Int) : IBinder.DeathRecipient { + // system_server dispatches the 3-argument callback up to Android 16 and the + // 4-argument one from Android 17 on. private val connection = object : android.app.IServiceConnection.Stub() { - // Android 8.1 ~ 16 override fun connected(name: ComponentName?, service: IBinder?, dead: Boolean) {} - // Android 17+ (new signature with IBinderSession) override fun connected( name: ComponentName?, service: IBinder?, diff --git a/hiddenapi/stubs/src/main/java/android/app/IBinderSession.java b/hiddenapi/stubs/src/main/java/android/app/IBinderSession.java index 1dc3d5777..7a81d5c08 100644 --- a/hiddenapi/stubs/src/main/java/android/app/IBinderSession.java +++ b/hiddenapi/stubs/src/main/java/android/app/IBinderSession.java @@ -1,35 +1,9 @@ package android.app; -import android.os.IBinder; import android.os.IInterface; /** - * Stub of {@code android.app.IBinderSession} introduced in Android 17 (API 36+). - * - *

This interface is used as a parameter of the new - * {@link IServiceConnection#connected} overload added in Android 17. On older - * Android versions this class does not exist at runtime, but because the stub - * is only referenced from the new {@code connected} overload (which is never - * invoked on older versions), the absence of the runtime class is not a - * problem — the method is never dispatched there. + * Stub of {@code android.app.IBinderSession}, added in Android 17 (API 37), + * where it appears as a parameter of {@link IServiceConnection#connected}. */ -public interface IBinderSession extends IInterface { - - String DESCRIPTOR = "android.app.IBinderSession"; - - void binderTransactionCompleted(long transactionId); - - long binderTransactionStarting(String name); - - abstract class Stub extends android.os.Binder implements IBinderSession { - - public static IBinderSession asInterface(IBinder obj) { - throw new UnsupportedOperationException(); - } - - @Override - public IBinder asBinder() { - throw new UnsupportedOperationException(); - } - } -} +public interface IBinderSession extends IInterface {} diff --git a/hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java b/hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java index 4bdcf7637..034ec8acc 100644 --- a/hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java +++ b/hiddenapi/stubs/src/main/java/android/app/IServiceConnection.java @@ -7,19 +7,10 @@ public interface IServiceConnection extends IInterface { - /** - * Old signature, used on Android 8.1 ~ 16. Removed from the framework - * interface in Android 17, but kept here so that code compiling against - * this stub can still override it (the method is simply never invoked on - * Android 17+). - */ + /** Declared by the framework up to Android 16. */ void connected(ComponentName name, IBinder service, boolean dead); - /** - * New signature introduced in Android 17 (API 36+). On older Android - * versions this overload does not exist at runtime and is never invoked, - * so overriding it is harmless there. - */ + /** Declared by the framework from Android 17 (API 37) on. */ void connected(ComponentName name, IBinder service, IBinderSession session, boolean dead); abstract class Stub extends Binder implements IServiceConnection {