Add fail-open LSPosed notification probe

This commit is contained in:
ajp_anton
2026-08-16 03:55:44 +00:00
parent af3673c2e8
commit 9c9ddb6350
7 changed files with 88 additions and 0 deletions
@@ -0,0 +1,54 @@
package se.ajpanton.notificationsmaster.module
import android.app.Notification
import android.os.Bundle
import android.util.Log
import io.github.libxposed.api.XposedInterface
import java.lang.reflect.Method
/**
* Milestone-one probe for the system-server notification path.
*
* It deliberately observes only this repository's test helper and always
* calls through. No notification is modified, cancelled, or suppressed.
*/
internal class NotificationEnqueueProbe(private val framework: XposedInterface) {
fun install(classLoader: ClassLoader) {
val managerClass = Class.forName(NOTIFICATION_MANAGER_SERVICE, false, classLoader)
val method = managerClass.declaredMethods.singleOrNull {
it.name == ENQUEUE_NOTIFICATION &&
it.returnType == Boolean::class.javaPrimitiveType &&
it.parameterTypes.any { type -> type.name.endsWith("PostNotificationTracker") }
} ?: error("No final $ENQUEUE_NOTIFICATION implementation found")
hook(method)
Log.i(TAG, "Installed fail-open notification enqueue probe")
}
private fun hook(method: Method) {
method.isAccessible = true
framework.hook(method).intercept { chain ->
logHelperNotification(chain.args)
chain.proceed()
}
}
private fun logHelperNotification(args: List<Any?>) {
val notification = args.filterIsInstance<Notification>().firstOrNull() ?: return
val packageName = args.filterIsInstance<String>().firstOrNull() ?: return
if (packageName != TEST_HELPER_PACKAGE) return
val extras: Bundle = notification.extras ?: Bundle.EMPTY
Log.i(
TAG,
"Observed helper notification before enqueue: title=${extras.getCharSequence(Notification.EXTRA_TITLE)} " +
"text=${extras.getCharSequence(Notification.EXTRA_TEXT)}",
)
}
private companion object {
const val TAG = "NotificationsMaster"
const val NOTIFICATION_MANAGER_SERVICE = "com.android.server.notification.NotificationManagerService"
const val ENQUEUE_NOTIFICATION = "enqueueNotificationInternal"
const val TEST_HELPER_PACKAGE = "se.ajpanton.notificationsmaster.helper"
}
}
@@ -0,0 +1,23 @@
package se.ajpanton.notificationsmaster.module
import android.util.Log
import io.github.libxposed.api.XposedModule
import io.github.libxposed.api.XposedModuleInterface
/**
* Optional libxposed entry point. It is inert unless the user enables this APK
* in a compatible framework and grants it a system-server scope.
*/
class NotificationsMasterModule : XposedModule() {
override fun onSystemServerStarting(param: XposedModuleInterface.SystemServerStartingParam) {
runCatching {
NotificationEnqueueProbe(this).install(param.classLoader)
}.onFailure { error ->
Log.e(TAG, "Notification interception probe was not installed; leaving Android unchanged", error)
}
}
private companion object {
const val TAG = "NotificationsMaster"
}
}
@@ -0,0 +1 @@
se.ajpanton.notificationsmaster.module.NotificationsMasterModule
@@ -0,0 +1,3 @@
minApiVersion=101
targetApiVersion=101
staticScope=true
@@ -0,0 +1,2 @@
android
system