Add fail-open LSPosed notification probe
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user