From 0cf9ca85724a51d5284e3b56c895bc1b0cdda09c Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 1 Apr 2026 10:01:13 +0200 Subject: [PATCH 01/10] feat(instrumentation): Add simple registry-based tracing for binder calls --- .../android/gradle/AndroidComponentsConfig.kt | 4 + .../gradle/extensions/BinderIpcExtension.kt | 10 + .../TracingInstrumentationExtension.kt | 6 + .../SpanAddingClassVisitorFactory.kt | 4 + .../instrumentation/binder/BinderIpc.kt | 32 ++ .../binder/BinderIpcMethodVisitor.kt | 119 +++++ .../binder/BinderMethodRegistry.kt | 423 ++++++++++++++++++ .../gradle/services/SentryModulesService.kt | 12 + .../io/sentry/android/gradle/util/Versions.kt | 1 + .../fakes/TestSpanAddingParameters.kt | 3 + .../gradle/util/SentryModulesCollectorTest.kt | 2 + 11 files changed, 616 insertions(+) create mode 100644 plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderIpcExtension.kt create mode 100644 plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt create mode 100644 plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitor.kt create mode 100644 plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistry.kt diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt index efc48c90b..daa7dfa85 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt @@ -203,6 +203,7 @@ fun ApplicationAndroidComponentsExtension.configure( extension.includeSourceContext, extension.dexguardEnabled, extension.tracingInstrumentation.appStart.enabled, + extension.tracingInstrumentation.binderIpc.enabled, ) /** * We have to register SentryModulesService as a build event listener, so it will not be @@ -235,6 +236,9 @@ fun ApplicationAndroidComponentsExtension.configure( params.appStartEnabled.setDisallowChanges( extension.tracingInstrumentation.appStart.enabled ) + params.binderIpcEnabled.setDisallowChanges( + extension.tracingInstrumentation.binderIpc.enabled + ) params.tmpDir.set(tmpDir) } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderIpcExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderIpcExtension.kt new file mode 100644 index 000000000..3247f94db --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderIpcExtension.kt @@ -0,0 +1,10 @@ +package io.sentry.android.gradle.extensions + +import javax.inject.Inject +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property + +open class BinderIpcExtension @Inject constructor(objects: ObjectFactory) { + /** Enables or disables Binder IPC call instrumentation. Defaults to true. */ + val enabled: Property = objects.property(Boolean::class.java).convention(true) +} diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt index d51ca26f4..10c9de7a8 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt @@ -71,6 +71,12 @@ open class TracingInstrumentationExtension @Inject constructor(objects: ObjectFa fun appStart(appStartExtensionAction: Action) { appStartExtensionAction.execute(appStart) } + + val binderIpc: BinderIpcExtension = objects.newInstance(BinderIpcExtension::class.java) + + fun binderIpc(binderIpcAction: Action) { + binderIpcAction.execute(binderIpc) + } } enum class InstrumentationFeature(val integrationName: String) { diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt index 18a8a1d02..f17b04964 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt @@ -13,6 +13,7 @@ import io.sentry.android.gradle.instrumentation.androidx.sqlite.database.Android import io.sentry.android.gradle.instrumentation.androidx.sqlite.statement.AndroidXSQLiteStatement import io.sentry.android.gradle.instrumentation.appstart.Application import io.sentry.android.gradle.instrumentation.appstart.ContentProvider +import io.sentry.android.gradle.instrumentation.binder.BinderIpc import io.sentry.android.gradle.instrumentation.logcat.Logcat import io.sentry.android.gradle.instrumentation.logcat.LogcatLevel import io.sentry.android.gradle.instrumentation.okhttp.OkHttp @@ -63,6 +64,8 @@ abstract class SpanAddingClassVisitorFactory : @get:Input val logcatEnabled: Property @get:Input val appStartEnabled: Property + + @get:Input val binderIpcEnabled: Property } private val instrumentable: ClassInstrumentable @@ -106,6 +109,7 @@ abstract class SpanAddingClassVisitorFactory : RemappingInstrumentable().takeIf { sentryModulesService.isFileIOInstrEnabled() }, ComposeNavigation().takeIf { sentryModulesService.isComposeInstrEnabled() }, Logcat().takeIf { sentryModulesService.isLogcatInstrEnabled() }, + BinderIpc().takeIf { sentryModulesService.isBinderIpcInstrEnabled() }, Application().takeIf { sentryModulesService.isAppStartInstrEnabled() }, ContentProvider().takeIf { sentryModulesService.isAppStartInstrEnabled() }, ) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt new file mode 100644 index 000000000..a95bbc196 --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt @@ -0,0 +1,32 @@ +package io.sentry.android.gradle.instrumentation.binder + +import com.android.build.api.instrumentation.ClassContext +import io.sentry.android.gradle.instrumentation.ClassInstrumentable +import io.sentry.android.gradle.instrumentation.CommonClassVisitor +import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory +import io.sentry.android.gradle.instrumentation.util.isSentryClass +import org.objectweb.asm.ClassVisitor + +class BinderIpc : ClassInstrumentable { + + companion object { + private const val CLASSNAME = "BinderIpc" + } + + override fun getVisitor( + instrumentableContext: ClassContext, + apiVersion: Int, + originalVisitor: ClassVisitor, + parameters: SpanAddingClassVisitorFactory.SpanAddingParameters, + ): ClassVisitor { + return CommonClassVisitor( + apiVersion, + originalVisitor, + CLASSNAME, + listOf(BinderIpcMethodInstrumentable()), + parameters, + ) + } + + override fun isInstrumentable(data: ClassContext) = !data.isSentryClass() +} diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitor.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitor.kt new file mode 100644 index 000000000..024d1ec4c --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitor.kt @@ -0,0 +1,119 @@ +package io.sentry.android.gradle.instrumentation.binder + +import io.sentry.android.gradle.instrumentation.MethodContext +import io.sentry.android.gradle.instrumentation.MethodInstrumentable +import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type +import org.objectweb.asm.commons.GeneratorAdapter +import org.objectweb.asm.commons.Method + +class BinderIpcMethodInstrumentable : MethodInstrumentable { + + override fun getVisitor( + instrumentableContext: MethodContext, + apiVersion: Int, + originalVisitor: MethodVisitor, + parameters: SpanAddingClassVisitorFactory.SpanAddingParameters, + ): MethodVisitor = BinderIpcMethodVisitor(apiVersion, originalVisitor, instrumentableContext) + + override fun isInstrumentable(data: MethodContext): Boolean = true +} + +private const val SENTRY_IPC_TRACER = "io/sentry/android/core/SentryIpcTracer" + +class BinderIpcMethodVisitor( + apiVersion: Int, + originalVisitor: MethodVisitor, + instrumentableContext: MethodContext, +) : + GeneratorAdapter( + apiVersion, + originalVisitor, + instrumentableContext.access, + instrumentableContext.name, + instrumentableContext.descriptor, + ) { + + override fun visitMethodInsn( + opcode: Int, + owner: String, + name: String, + descriptor: String, + isInterface: Boolean, + ) { + val spec = BinderMethodRegistry.lookup(owner, name) + if (spec == null) { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) + return + } + + val isInstanceCall = opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKEINTERFACE + val isStaticCall = opcode == Opcodes.INVOKESTATIC + if (spec.isStatic && !isStaticCall || !spec.isStatic && !isInstanceCall) { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) + return + } + + val argTypes = Method(name, descriptor).argumentTypes + + // Save arguments from the stack into temp locals (reverse order for LIFO) + val argLocals = IntArray(argTypes.size) + for (i in argLocals.size - 1 downTo 0) { + argLocals[i] = newLocal(argTypes[i]) + storeLocal(argLocals[i]) + } + + val receiverLocal = + if (!spec.isStatic) { + val local = newLocal(Type.getObjectType(owner)) + storeLocal(local) + local + } else { + -1 + } + + mv.visitLdcInsn(spec.component) + mv.visitLdcInsn(name) + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + SENTRY_IPC_TRACER, + "onCallStart", + "(Ljava/lang/String;Ljava/lang/String;)I", + false, + ) + val cookieLocal = newLocal(Type.INT_TYPE) + storeLocal(cookieLocal) + + val tryStart = Label() + val tryEnd = Label() + val catchHandler = Label() + val afterFinally = Label() + + mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, null) + mv.visitLabel(tryStart) + + if (!spec.isStatic) { + loadLocal(receiverLocal) + } + for (local in argLocals) { + loadLocal(local) + } + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) + + mv.visitLabel(tryEnd) + loadLocal(cookieLocal) + mv.visitMethodInsn(Opcodes.INVOKESTATIC, SENTRY_IPC_TRACER, "onCallEnd", "(I)V", false) + mv.visitJumpInsn(Opcodes.GOTO, afterFinally) + + // catch-all handler: call onCallEnd then re-throw + mv.visitLabel(catchHandler) + loadLocal(cookieLocal) + mv.visitMethodInsn(Opcodes.INVOKESTATIC, SENTRY_IPC_TRACER, "onCallEnd", "(I)V", false) + mv.visitInsn(Opcodes.ATHROW) + + mv.visitLabel(afterFinally) + } +} diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistry.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistry.kt new file mode 100644 index 000000000..ed1e5bde0 --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistry.kt @@ -0,0 +1,423 @@ +package io.sentry.android.gradle.instrumentation.binder + +data class BinderMethodSpec( + val owner: String, + val name: String, + val component: String, + val isStatic: Boolean = false, +) + +object BinderMethodRegistry { + + private val registry: Map> = buildRegistry() + + fun lookup(owner: String, name: String): BinderMethodSpec? { + val specs = registry[owner] ?: return null + return specs.find { it.name == name } + } + + @Suppress("LongMethod") + private fun buildRegistry(): Map> { + val specs = mutableListOf() + + specs.addAll( + "android/content/ContentResolver", + "ContentResolver", + "query", + "insert", + "update", + "delete", + "call", + "bulkInsert", + "openInputStream", + "openOutputStream", + "openAssetFileDescriptor", + "openFileDescriptor", + "acquireContentProviderClient", + "registerContentObserver", + "getType", + ) + + specs.addAll( + "android/content/pm/PackageManager", + "PackageManager", + "getInstalledPackages", + "getPackageInfo", + "resolveActivity", + "queryIntentActivities", + "getInstalledApplications", + "resolveService", + "queryIntentServices", + "getApplicationInfo", + "getActivityInfo", + "getServiceInfo", + "getReceiverInfo", + "getProviderInfo", + "checkPermission", + "hasSystemFeature", + "getLaunchIntentForPackage", + "getComponentEnabledSetting", + "setComponentEnabledSetting", + "getPackagesForUid", + "getInstallerPackageName", + "getInstallSourceInfo", + ) + + for (settingsClass in + listOf( + "android/provider/Settings\$Secure", + "android/provider/Settings\$Global", + "android/provider/Settings\$System", + )) { + val component = "Settings." + settingsClass.substringAfterLast("\$") + for (method in listOf("getString", "getInt", "getLong", "getFloat", "putString", "putInt")) { + specs.add(BinderMethodSpec(settingsClass, method, component, isStatic = true)) + } + } + + for (ctx in listOf("android/content/Context", "android/content/ContextWrapper")) { + specs.addAll( + ctx, + "Context", + "startService", + "stopService", + "bindService", + "unbindService", + "sendBroadcast", + "sendOrderedBroadcast", + "startActivity", + "startActivities", + "startForegroundService", + "registerReceiver", + "unregisterReceiver", + "checkSelfPermission", + "checkPermission", + ) + } + + specs.addAll( + "android/net/ConnectivityManager", + "ConnectivityManager", + "getActiveNetworkInfo", + "getActiveNetwork", + "getNetworkCapabilities", + "getAllNetworks", + "isActiveNetworkMetered", + "registerDefaultNetworkCallback", + "registerNetworkCallback", + ) + + specs.addAll( + "android/app/ActivityManager", + "ActivityManager", + "getRunningAppProcesses", + "getMemoryInfo", + "getRunningServices", + "getProcessMemoryInfo", + ) + + specs.addAll( + "android/view/inputmethod/InputMethodManager", + "InputMethodManager", + "showSoftInput", + "hideSoftInputFromWindow", + "restartInput", + "isActive", + ) + + specs.addAll( + "android/hardware/camera2/CameraManager", + "CameraManager", + "getCameraIdList", + "getCameraCharacteristics", + "openCamera", + ) + + specs.addAll( + "android/os/PowerManager", + "PowerManager", + "isInteractive", + "isDeviceIdleMode", + "isPowerSaveMode", + ) + specs.addAll("android/os/PowerManager\$WakeLock", "PowerManager.WakeLock", "acquire", "release") + + specs.addAll( + "android/location/LocationManager", + "LocationManager", + "getLastKnownLocation", + "requestLocationUpdates", + "getProviders", + ) + + specs.addAll( + "android/telephony/TelephonyManager", + "TelephonyManager", + "getDeviceId", + "getNetworkOperator", + "getSimOperator", + "getNetworkOperatorName", + "getSimOperatorName", + "getLine1Number", + "getSubscriberId", + "getNetworkType", + "getDataNetworkType", + "getAllCellInfo", + ) + + specs.addAll( + "android/net/wifi/WifiManager", + "WifiManager", + "getConnectionInfo", + "isWifiEnabled", + "setWifiEnabled", + "getScanResults", + "startScan", + "getConfiguredNetworks", + "addNetwork", + "removeNetwork", + "disconnect", + "reconnect", + "reassociate", + "getDhcpInfo", + "getWifiState", + ) + + specs.addAll( + "android/bluetooth/BluetoothAdapter", + "BluetoothAdapter", + "isEnabled", + "getState", + "getName", + "getAddress", + "getBondedDevices", + "startDiscovery", + "cancelDiscovery", + "isDiscovering", + "enable", + "disable", + "getScanMode", + "setScanMode", + ) + + specs.addAll( + "android/bluetooth/BluetoothDevice", + "BluetoothDevice", + "getName", + "getBondState", + "getType", + "createBond", + "removeBond", + "connectGatt", + "getBatteryLevel", + "getUuids", + ) + + specs.addAll( + "android/bluetooth/BluetoothGatt", + "BluetoothGatt", + "connect", + "disconnect", + "discoverServices", + "readCharacteristic", + "writeCharacteristic", + "readDescriptor", + "writeDescriptor", + "readRemoteRssi", + "requestMtu", + ) + + specs.addAll( + "android/bluetooth/BluetoothManager", + "BluetoothManager", + "getConnectedDevices", + "getConnectionState", + "getDevicesMatchingConnectionStates", + "openGattServer", + ) + + specs.addAll( + "android/media/AudioManager", + "AudioManager", + "getStreamVolume", + "getStreamMaxVolume", + "setStreamVolume", + "getRingerMode", + "setRingerMode", + "requestAudioFocus", + "abandonAudioFocus", + "getMode", + "setMode", + "isMusicActive", + "isBluetoothA2dpOn", + "isBluetoothScoOn", + ) + + specs.addAll( + "android/content/ClipboardManager", + "ClipboardManager", + "getPrimaryClip", + "setPrimaryClip", + "hasPrimaryClip", + ) + + specs.addAll( + "android/app/NotificationManager", + "NotificationManager", + "notify", + "cancel", + "cancelAll", + "getActiveNotifications", + ) + + specs.addAll( + "android/app/AlarmManager", + "AlarmManager", + "set", + "setExact", + "setRepeating", + "setWindow", + "cancel", + ) + + specs.addAll( + "android/app/KeyguardManager", + "KeyguardManager", + "isKeyguardLocked", + "isDeviceLocked", + "isKeyguardSecure", + ) + + specs.addAll( + "android/accounts/AccountManager", + "AccountManager", + "getAccounts", + "getAccountsByType", + "getAuthToken", + ) + + specs.addAll("android/os/UserManager", "UserManager", "getUserProfiles", "isUserUnlocked") + + specs.addAll( + "android/hardware/display/DisplayManager", + "DisplayManager", + "getDisplays", + "getDisplay", + ) + + specs.addAll( + "android/os/Vibrator", + "Vibrator", + "vibrate", + "cancel", + "hasVibrator", + "hasAmplitudeControl", + ) + + specs.addAll( + "android/os/VibratorManager", + "VibratorManager", + "getVibratorIds", + "getDefaultVibrator", + "vibrate", + "cancel", + ) + + specs.addAll( + "android/app/job/JobScheduler", + "JobScheduler", + "schedule", + "enqueue", + "cancel", + "cancelAll", + "getAllPendingJobs", + "getPendingJob", + ) + + specs.addAll( + "android/content/pm/ShortcutManager", + "ShortcutManager", + "setDynamicShortcuts", + "addDynamicShortcuts", + "removeDynamicShortcuts", + "removeAllDynamicShortcuts", + "getDynamicShortcuts", + "getPinnedShortcuts", + "updateShortcuts", + "requestPinShortcut", + "isRequestPinShortcutSupported", + "pushDynamicShortcut", + ) + + specs.addAll( + "android/app/AppOpsManager", + "AppOpsManager", + "checkOp", + "checkOpNoThrow", + "noteOp", + "noteOpNoThrow", + "noteProxyOp", + "noteProxyOpNoThrow", + "startOp", + "startOpNoThrow", + "finishOp", + "checkPackage", + ) + + specs.addAll( + "android/os/storage/StorageManager", + "StorageManager", + "getStorageVolumes", + "getPrimaryStorageVolume", + "getAllocatableBytes", + "getCacheSizeBytes", + ) + + specs.addAll( + "android/telephony/SubscriptionManager", + "SubscriptionManager", + "getActiveSubscriptionInfoList", + "getActiveSubscriptionInfo", + "getActiveSubscriptionInfoCount", + "getActiveSubscriptionInfoForSimSlotIndex", + ) + + specs.addAll( + "android/app/WallpaperManager", + "WallpaperManager", + "getDrawable", + "peekDrawable", + "getWallpaperColors", + "setResource", + "setBitmap", + "getDesiredMinimumWidth", + "getDesiredMinimumHeight", + "isWallpaperSupported", + "isSetWallpaperAllowed", + ) + + val strictMode = "android/os/StrictMode" + for (method in + listOf( + "setThreadPolicy", + "setThreadPolicyMask", + "allowThreadDiskWrites", + "allowThreadDiskReads", + "allowThreadViolations", + )) { + specs.add(BinderMethodSpec(strictMode, method, "StrictMode", isStatic = true)) + } + + return specs.groupBy { it.owner } + } + + private fun MutableList.addAll( + owner: String, + component: String, + vararg methods: String, + ) { + for (method in methods) { + add(BinderMethodSpec(owner, method, component)) + } + } +} diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/services/SentryModulesService.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/services/SentryModulesService.kt index 29ba71bb3..2ce5d360d 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/services/SentryModulesService.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/services/SentryModulesService.kt @@ -44,6 +44,10 @@ abstract class SentryModulesService : features.add("AppStartInstrumentation") } + if (isBinderIpcInstrEnabled()) { + features.add("BinderIpcInstrumentation") + } + if (parameters.sourceContextEnabled.getOrElse(false)) { features.add("SourceContext") } @@ -116,6 +120,10 @@ abstract class SentryModulesService : sentryModules.isAtLeast(SentryModules.SENTRY_ANDROID_CORE, SentryVersions.VERSION_APP_START) && parameters.appStartEnabled.get() + fun isBinderIpcInstrEnabled(): Boolean = + sentryModules.isAtLeast(SentryModules.SENTRY_ANDROID_CORE, SentryVersions.VERSION_BINDER_IPC) && + parameters.binderIpcEnabled.get() + private fun Map.isAtLeast( module: ModuleIdentifier, minVersion: SemVer, @@ -129,6 +137,7 @@ abstract class SentryModulesService : sourceContextEnabled: Provider, dexguardEnabled: Provider, appStartEnabled: Provider, + binderIpcEnabled: Provider, ): Provider { return project.gradle.sharedServices.registerIfAbsent( getBuildServiceName(SentryModulesService::class.java), @@ -139,6 +148,7 @@ abstract class SentryModulesService : it.parameters.sourceContextEnabled.setDisallowChanges(sourceContextEnabled) it.parameters.dexguardEnabled.setDisallowChanges(dexguardEnabled) it.parameters.appStartEnabled.setDisallowChanges(appStartEnabled) + it.parameters.binderIpcEnabled.setDisallowChanges(binderIpcEnabled) } } } @@ -156,5 +166,7 @@ abstract class SentryModulesService : @get:Input val dexguardEnabled: Property @get:Input val appStartEnabled: Property + + @get:Input val binderIpcEnabled: Property } } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt index 222756174..c93eadb0c 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt @@ -33,6 +33,7 @@ internal object SentryVersions { internal val VERSION_SQLITE = SemVer(6, 21, 0) internal val VERSION_ANDROID_OKHTTP_LISTENER = SemVer(6, 20, 0) internal val VERSION_OKHTTP = SemVer(7, 0, 0) + internal val VERSION_BINDER_IPC = SemVer(8, 36, 0) } internal object SentryModules { diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/fakes/TestSpanAddingParameters.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/fakes/TestSpanAddingParameters.kt index cfe4c0d23..dbc56b44e 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/fakes/TestSpanAddingParameters.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/fakes/TestSpanAddingParameters.kt @@ -43,4 +43,7 @@ class TestSpanAddingParameters( override val appStartEnabled: Property get() = TODO() + + override val binderIpcEnabled: Property + get() = TODO() } diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/util/SentryModulesCollectorTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/util/SentryModulesCollectorTest.kt index c2f728d81..74ee2ceff 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/util/SentryModulesCollectorTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/util/SentryModulesCollectorTest.kt @@ -57,6 +57,7 @@ class SentryModulesCollectorTest { val sourceContextEnabled = fakeProject.provider { true } val dexguardEnabled = fakeProject.provider { true } val appStartEnabled = fakeProject.provider { true } + val binderIpcEnabled = fakeProject.provider { true } val project = spy(fakeProject) whenever(project.logger).thenReturn(logger) @@ -69,6 +70,7 @@ class SentryModulesCollectorTest { sourceContextEnabled, dexguardEnabled, appStartEnabled, + binderIpcEnabled, ) return project From 84cae2a1db598b908594d39b46c1456c53d7d4af Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 22 Apr 2026 11:54:58 +0200 Subject: [PATCH 02/10] feat(instrumentation): Gate binder IPC instrumentation on SentryIpcTracer adapter presence The SentryIpcTracer adapter class is introduced in sentry-java 8.40.0 (getsentry/sentry-java#5326). Bump VERSION_BINDER_IPC so the instrumentation no-ops when the adapter is not present on the classpath. Adds unit test coverage for the registry lookup and the ASM visitor (tracer wrapping, static vs instance handling, non-binder pass-through). Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 6 + .../io/sentry/android/gradle/util/Versions.kt | 2 +- .../binder/BinderIpcMethodVisitorTest.kt | 144 ++++++++++++++++++ .../binder/BinderMethodRegistryTest.kt | 43 ++++++ .../instrumentation/binder/SyntheticClass.kt | 20 +++ 5 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitorTest.kt create mode 100644 plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistryTest.kt create mode 100644 plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/SyntheticClass.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ba0c8187..001c1d1b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- Add Binder IPC call instrumentation behind `tracingInstrumentation.binderIpc.enabled` (default on). The instrumentation is a no-op unless `sentry-android-core` >= 8.40.0 is on the classpath, which provides the required `SentryIpcTracer` adapter class. + ## 6.4.0-alpha.4 ### Internal Changes 🔧 diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt index c93eadb0c..09ec8d638 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt @@ -33,7 +33,7 @@ internal object SentryVersions { internal val VERSION_SQLITE = SemVer(6, 21, 0) internal val VERSION_ANDROID_OKHTTP_LISTENER = SemVer(6, 20, 0) internal val VERSION_OKHTTP = SemVer(7, 0, 0) - internal val VERSION_BINDER_IPC = SemVer(8, 36, 0) + internal val VERSION_BINDER_IPC = SemVer(8, 40, 0) } internal object SentryModules { diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitorTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitorTest.kt new file mode 100644 index 000000000..72a30e381 --- /dev/null +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitorTest.kt @@ -0,0 +1,144 @@ +package io.sentry.android.gradle.instrumentation.binder + +import io.sentry.android.gradle.instrumentation.CommonClassVisitor +import io.sentry.android.gradle.instrumentation.fakes.TestSpanAddingParameters +import java.io.PrintWriter +import java.io.StringWriter +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Opcodes +import org.objectweb.asm.util.Textifier +import org.objectweb.asm.util.TraceClassVisitor + +class BinderIpcMethodVisitorTest { + + @get:Rule val tmpDir = TemporaryFolder() + + private fun instrument(classBytes: ByteArray): ByteArray { + val reader = ClassReader(classBytes) + val writer = ClassWriter(reader, ClassWriter.COMPUTE_MAXS) + val visitor = + CommonClassVisitor( + Opcodes.ASM9, + writer, + "TestClass", + listOf(BinderIpcMethodInstrumentable()), + TestSpanAddingParameters(debugOutput = false, inMemoryDir = tmpDir.root), + ) + reader.accept(visitor, ClassReader.SKIP_FRAMES) + return writer.toByteArray() + } + + private fun disassemble(bytes: ByteArray): String { + val sw = StringWriter() + ClassReader(bytes).accept(TraceClassVisitor(null, Textifier(), PrintWriter(sw)), 0) + return sw.toString() + } + + @Test + fun `wraps known instance binder call with tracer start and end`() { + // Method body: ContentResolver.query(uri, null, null, null, null) + val bytes = + SyntheticClass.build("callQuery", "(Landroid/content/ContentResolver;Landroid/net/Uri;)V") { + visitVarInsn(Opcodes.ALOAD, 0) // resolver + visitVarInsn(Opcodes.ALOAD, 1) // uri + visitInsn(Opcodes.ACONST_NULL) + visitInsn(Opcodes.ACONST_NULL) + visitInsn(Opcodes.ACONST_NULL) + visitInsn(Opcodes.ACONST_NULL) + visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + "android/content/ContentResolver", + "query", + "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", + false, + ) + visitInsn(Opcodes.POP) + visitInsn(Opcodes.RETURN) + } + + val instrumented = instrument(bytes) + val text = disassemble(instrumented) + + assertTrue( + text.contains("io/sentry/android/core/SentryIpcTracer.onCallStart"), + "onCallStart should be emitted:\n$text", + ) + assertTrue( + text.contains("io/sentry/android/core/SentryIpcTracer.onCallEnd"), + "onCallEnd should be emitted:\n$text", + ) + assertTrue( + text.contains("LDC \"ContentResolver\""), + "component constant should be pushed:\n$text", + ) + assertTrue(text.contains("LDC \"query\""), "method name constant should be pushed:\n$text") + assertTrue(text.contains("android/content/ContentResolver.query")) + assertTrue(text.contains("TRYCATCHBLOCK"), "try/catch handler should be emitted:\n$text") + } + + @Test + fun `wraps known static binder call`() { + val bytes = + SyntheticClass.build("callSettings", "()Ljava/lang/String;") { + visitInsn(Opcodes.ACONST_NULL) // resolver + visitLdcInsn("some_key") + visitMethodInsn( + Opcodes.INVOKESTATIC, + "android/provider/Settings\$Secure", + "getString", + "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;", + false, + ) + visitInsn(Opcodes.ARETURN) + } + + val instrumented = instrument(bytes) + val text = disassemble(instrumented) + + assertTrue(text.contains("io/sentry/android/core/SentryIpcTracer.onCallStart")) + assertTrue(text.contains("LDC \"Settings.Secure\"")) + } + + @Test + fun `does not wrap unknown method calls`() { + val bytes = + SyntheticClass.build("callUnknown", "(Ljava/lang/String;)I") { + visitVarInsn(Opcodes.ALOAD, 1) + visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "length", "()I", false) + visitInsn(Opcodes.IRETURN) + } + + val instrumented = instrument(bytes) + val text = disassemble(instrumented) + + assertFalse(text.contains("SentryIpcTracer"), "unknown calls must not be wrapped:\n$text") + } + + @Test + fun `does not wrap when opcode does not match registry kind`() { + // Registry marks ContentResolver.query as instance-only; emit as INVOKESTATIC and expect no + // wrap + val bytes = + SyntheticClass.build("callStaticQuery", "()V") { + visitMethodInsn( + Opcodes.INVOKESTATIC, + "android/content/ContentResolver", + "query", + "()V", + false, + ) + visitInsn(Opcodes.RETURN) + } + + val instrumented = instrument(bytes) + val text = disassemble(instrumented) + + assertFalse(text.contains("SentryIpcTracer")) + } +} diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistryTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistryTest.kt new file mode 100644 index 000000000..171842043 --- /dev/null +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistryTest.kt @@ -0,0 +1,43 @@ +package io.sentry.android.gradle.instrumentation.binder + +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +import org.junit.Test + +class BinderMethodRegistryTest { + + @Test + fun `lookup returns null for unknown owner`() { + assertNull(BinderMethodRegistry.lookup("com/example/Foo", "bar")) + } + + @Test + fun `lookup returns null for known owner but unknown method`() { + assertNull(BinderMethodRegistry.lookup("android/content/ContentResolver", "unknownMethod")) + } + + @Test + fun `lookup returns spec for known instance binder method`() { + val spec = BinderMethodRegistry.lookup("android/content/ContentResolver", "query") + assertNotNull(spec) + assertEquals("ContentResolver", spec.component) + assertFalse(spec.isStatic) + } + + @Test + fun `lookup returns spec for known static binder method`() { + val spec = BinderMethodRegistry.lookup("android/provider/Settings\$Secure", "getString") + assertNotNull(spec) + assertEquals("Settings.Secure", spec.component) + assertTrue(spec.isStatic) + } + + @Test + fun `lookup covers Context subtypes separately`() { + assertNotNull(BinderMethodRegistry.lookup("android/content/Context", "startService")) + assertNotNull(BinderMethodRegistry.lookup("android/content/ContextWrapper", "startService")) + } +} diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/SyntheticClass.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/SyntheticClass.kt new file mode 100644 index 000000000..7a934c4b8 --- /dev/null +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/SyntheticClass.kt @@ -0,0 +1,20 @@ +package io.sentry.android.gradle.instrumentation.binder + +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes + +internal object SyntheticClass { + + fun build(methodName: String, descriptor: String, body: MethodVisitor.() -> Unit): ByteArray { + val cw = ClassWriter(ClassWriter.COMPUTE_MAXS or ClassWriter.COMPUTE_FRAMES) + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "TestClass", null, "java/lang/Object", null) + val mv = cw.visitMethod(Opcodes.ACC_PUBLIC, methodName, descriptor, null, null) + mv.visitCode() + mv.body() + mv.visitMaxs(0, 0) + mv.visitEnd() + cw.visitEnd() + return cw.toByteArray() + } +} From 2a204f6b31506e9a2476df4a860d991a3b078059 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 22 Apr 2026 11:57:25 +0200 Subject: [PATCH 03/10] chore: Fix changelog entry style and add PR link Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 001c1d1b2..9e6a039a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- Add Binder IPC call instrumentation behind `tracingInstrumentation.binderIpc.enabled` (default on). The instrumentation is a no-op unless `sentry-android-core` >= 8.40.0 is on the classpath, which provides the required `SentryIpcTracer` adapter class. +- Add Binder IPC call instrumentation ([#1159](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1159)) ## 6.4.0-alpha.4 From c88ce04a63cae284c6af8ddd86dc0f67c04102d9 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 3 Jun 2026 14:33:10 +0200 Subject: [PATCH 04/10] Extend registry --- .../binder/BinderMethodRegistry.kt | 2078 +++++++++++++++-- 1 file changed, 1877 insertions(+), 201 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistry.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistry.kt index ed1e5bde0..6354b934d 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistry.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodRegistry.kt @@ -20,6 +20,8 @@ object BinderMethodRegistry { private fun buildRegistry(): Map> { val specs = mutableListOf() + // region content & package management + specs.addAll( "android/content/ContentResolver", "ContentResolver", @@ -96,304 +98,1572 @@ object BinderMethodRegistry { } specs.addAll( - "android/net/ConnectivityManager", - "ConnectivityManager", - "getActiveNetworkInfo", - "getActiveNetwork", - "getNetworkCapabilities", - "getAllNetworks", - "isActiveNetworkMetered", - "registerDefaultNetworkCallback", - "registerNetworkCallback", + "android/content/ClipboardManager", + "ClipboardManager", + "setPrimaryClip", + "clearPrimaryClip", + "getPrimaryClip", + "getPrimaryClipDescription", + "hasPrimaryClip", + "addPrimaryClipChangedListener", + "removePrimaryClipChangedListener", + "hasText", ) specs.addAll( - "android/app/ActivityManager", - "ActivityManager", - "getRunningAppProcesses", - "getMemoryInfo", - "getRunningServices", - "getProcessMemoryInfo", + "android/content/pm/LauncherApps", + "LauncherApps", + "getProfiles", + "getActivityList", + "resolveActivity", + "startMainActivity", + "startPackageInstallerSessionDetailsActivity", + "startAppDetailsActivity", + "getShortcutIntent", + "getShortcutConfigActivityList", + "getShortcutConfigActivityIntent", + "isPackageEnabled", + "getSuspendedPackageLauncherExtras", + "shouldHideFromSuggestions", + "getApplicationInfo", + "isActivityEnabled", + "hasShortcutHostPermission", + "getShortcuts", + "pinShortcuts", + "startShortcut", + "registerCallback", + "unregisterCallback", + "setArchiveCompatibility", + "registerPackageInstallerSessionCallback", + "getAllPackageInstallerSessions", ) - specs.addAll( - "android/view/inputmethod/InputMethodManager", - "InputMethodManager", - "showSoftInput", - "hideSoftInputFromWindow", - "restartInput", - "isActive", + "android/content/pm/LauncherApps\$PinItemRequest", + "PinItemRequest", + "getShortcutInfo", + "getAppWidgetProviderInfo", + "getExtras", + "isValid", + "accept", ) specs.addAll( - "android/hardware/camera2/CameraManager", - "CameraManager", - "getCameraIdList", - "getCameraCharacteristics", - "openCamera", + "android/content/pm/ShortcutManager", + "ShortcutManager", + "setDynamicShortcuts", + "getDynamicShortcuts", + "getManifestShortcuts", + "getShortcuts", + "addDynamicShortcuts", + "removeDynamicShortcuts", + "removeAllDynamicShortcuts", + "removeLongLivedShortcuts", + "getPinnedShortcuts", + "updateShortcuts", + "disableShortcuts", + "enableShortcuts", + "getMaxShortcutCountPerActivity", + "isRateLimitingActive", + "getIconMaxWidth", + "getIconMaxHeight", + "reportShortcutUsed", + "isRequestPinShortcutSupported", + "requestPinShortcut", + "createShortcutResultIntent", + "pushDynamicShortcut", ) specs.addAll( - "android/os/PowerManager", - "PowerManager", - "isInteractive", - "isDeviceIdleMode", - "isPowerSaveMode", + "android/content/RestrictionsManager", + "RestrictionsManager", + "getApplicationRestrictions", + "getApplicationRestrictionsPerAdmin", + "hasRestrictionsProvider", + "requestPermission", + "createLocalApprovalIntent", + "notifyPermissionResponse", ) - specs.addAll("android/os/PowerManager\$WakeLock", "PowerManager.WakeLock", "acquire", "release") - specs.addAll( - "android/location/LocationManager", - "LocationManager", - "getLastKnownLocation", - "requestLocationUpdates", - "getProviders", - ) + specs.addAll("android/content/om/OverlayManager", "OverlayManager", "commit") specs.addAll( - "android/telephony/TelephonyManager", - "TelephonyManager", - "getDeviceId", - "getNetworkOperator", - "getSimOperator", - "getNetworkOperatorName", - "getSimOperatorName", - "getLine1Number", - "getSubscriberId", - "getNetworkType", - "getDataNetworkType", - "getAllCellInfo", + "android/content/pm/PackageInstaller", + "PackageInstaller", + "createSession", + "openSession", + "updateSessionAppIcon", + "updateSessionAppLabel", + "abandonSession", + "getSessionInfo", + "getAllSessions", + "getMySessions", + "getStagedSessions", + "uninstall", + "installExistingPackage", + "uninstallExistingPackage", + "installPackageArchived", + "registerSessionCallback", + "unregisterSessionCallback", + "checkInstallConstraints", + "waitForInstallConstraints", + "commitSessionAfterInstallConstraintsAreMet", + "requestArchive", + "requestUnarchive", + "reportUnarchivalStatus", + "reportUnarchivalState", ) - specs.addAll( - "android/net/wifi/WifiManager", - "WifiManager", - "getConnectionInfo", - "isWifiEnabled", - "setWifiEnabled", - "getScanResults", - "startScan", - "getConfiguredNetworks", - "addNetwork", - "removeNetwork", - "disconnect", - "reconnect", - "reassociate", - "getDhcpInfo", - "getWifiState", + "android/content/pm/PackageInstaller\$Session", + "PackageInstaller.Session", + "setStagingProgress", + "openWrite", + "getNames", + "openRead", + "removeSplit", + "setChecksums", + "requestChecksums", + "commit", + "transfer", + "close", + "abandon", + "isMultiPackage", + "isStaged", + "getParentSessionId", + "getChildSessionIds", + "addChildSessionId", + "removeChildSessionId", + "getAppMetadata", + "setAppMetadata", + "requestUserPreapproval", + "isApplicationEnabledSettingPersistent", + "isRequestUpdateOwnership", ) specs.addAll( - "android/bluetooth/BluetoothAdapter", - "BluetoothAdapter", - "isEnabled", - "getState", - "getName", - "getAddress", - "getBondedDevices", - "startDiscovery", - "cancelDiscovery", - "isDiscovering", - "enable", - "disable", - "getScanMode", - "setScanMode", + "android/content/pm/CrossProfileApps", + "CrossProfileApps", + "startMainActivity", + "startActivity", + "getTargetUserProfiles", + "isProfile", + "isManagedProfile", + "getProfileSwitchingLabel", + "getProfileSwitchingIconDrawable", + "canRequestInteractAcrossProfiles", + "canInteractAcrossProfiles", + "createRequestInteractAcrossProfilesIntent", ) + // endregion + + // region activity, jobs & app lifecycle + specs.addAll( - "android/bluetooth/BluetoothDevice", - "BluetoothDevice", - "getName", - "getBondState", - "getType", - "createBond", - "removeBond", - "connectGatt", - "getBatteryLevel", - "getUuids", + "android/app/ActivityManager", + "ActivityManager", + "addAppTask", + "addApplicationStartInfoCompletionListener", + "addStartInfoTimestamp", + "appNotResponding", + "clearApplicationUserData", + "clearWatchHeapLimit", + "getAppTaskThumbnailSize", + "getAppTasks", + "getDeviceConfigurationInfo", + "getHistoricalProcessExitReasons", + "getHistoricalProcessStartReasons", + "getLockTaskModeState", + "getMemoryInfo", + "getMyMemoryState", + "getProcessMemoryInfo", + "getProcessesInErrorState", + "getRecentTasks", + "getRunningAppProcesses", + "getRunningServiceControlPanel", + "getRunningServices", + "getRunningTasks", + "isActivityStartAllowedOnDisplay", + "isBackgroundRestricted", + "isInLockTaskMode", + "isLowMemoryKillReportSupported", + "isRunningInTestHarness", + "isRunningInUserTestHarness", + "isUserAMonkey", + "killBackgroundProcesses", + "moveTaskToFront", + "removeApplicationStartInfoCompletionListener", + "restartPackage", + "setProcessStateSummary", + "setVrThread", + "setWatchHeapLimit", ) specs.addAll( - "android/bluetooth/BluetoothGatt", - "BluetoothGatt", - "connect", - "disconnect", - "discoverServices", - "readCharacteristic", - "writeCharacteristic", - "readDescriptor", - "writeDescriptor", - "readRemoteRssi", - "requestMtu", + "android/app/job/JobScheduler", + "JobScheduler", + "schedule", + "enqueue", + "cancel", + "cancelAll", + "cancelInAllNamespaces", + "getAllPendingJobs", + "getPendingJobsInAllNamespaces", + "getPendingJob", + "getPendingJobReason", + "getPendingJobReasons", + "getPendingJobReasonsHistory", ) specs.addAll( - "android/bluetooth/BluetoothManager", - "BluetoothManager", - "getConnectedDevices", - "getConnectionState", - "getDevicesMatchingConnectionStates", - "openGattServer", + "android/app/usage/UsageStatsManager", + "UsageStatsManager", + "queryUsageStats", + "queryConfigurations", + "queryEventStats", + "queryEvents", + "queryEventsForSelf", + "queryAndAggregateUsageStats", + "isAppInactive", + "getAppStandbyBucket", ) specs.addAll( - "android/media/AudioManager", - "AudioManager", - "getStreamVolume", - "getStreamMaxVolume", - "setStreamVolume", - "getRingerMode", - "setRingerMode", - "requestAudioFocus", - "abandonAudioFocus", - "getMode", - "setMode", - "isMusicActive", - "isBluetoothA2dpOn", - "isBluetoothScoOn", + "android/app/usage/StorageStatsManager", + "StorageStatsManager", + "getTotalBytes", + "getFreeBytes", + "queryStatsForPackage", + "queryStatsForUid", + "queryStatsForUser", + "queryExternalStatsForUser", ) specs.addAll( - "android/content/ClipboardManager", - "ClipboardManager", - "getPrimaryClip", - "setPrimaryClip", - "hasPrimaryClip", + "android/app/people/PeopleManager", + "PeopleManager", + "addOrUpdateStatus", + "clearStatus", + "clearStatuses", + "getStatuses", ) + specs.addAll("android/app/PendingIntent", "PendingIntent", "send") + + // endregion + + // region notifications, alarms & background scheduling + specs.addAll( "android/app/NotificationManager", "NotificationManager", "notify", + "notifyAsPackage", "cancel", "cancelAll", + "setNotificationDelegate", + "getNotificationDelegate", + "canNotifyAsPackage", + "canUseFullScreenIntent", + "canPostPromotedNotifications", + "createNotificationChannelGroup", + "createNotificationChannelGroups", + "createNotificationChannel", + "createNotificationChannels", + "getNotificationChannel", + "getNotificationChannels", + "deleteNotificationChannel", + "getNotificationChannelGroup", + "getNotificationChannelGroups", + "deleteNotificationChannelGroup", + "getConsolidatedNotificationPolicy", + "areAutomaticZenRulesUserManaged", + "getAutomaticZenRules", + "getAutomaticZenRule", + "addAutomaticZenRule", + "updateAutomaticZenRule", + "getAutomaticZenRuleState", + "setAutomaticZenRuleState", + "removeAutomaticZenRule", + "getImportance", + "areNotificationsEnabled", + "areBubblesAllowed", + "areBubblesEnabled", + "getBubblePreference", + "areNotificationsPaused", + "isNotificationPolicyAccessGranted", + "isNotificationListenerAccessGranted", + "shouldHideSilentStatusBarIcons", + "getNotificationPolicy", + "setNotificationPolicy", "getActiveNotifications", + "getCurrentInterruptionFilter", + "setInterruptionFilter", ) specs.addAll( "android/app/AlarmManager", "AlarmManager", "set", - "setExact", "setRepeating", "setWindow", + "setExact", + "setAlarmClock", + "setInexactRepeating", + "setAndAllowWhileIdle", + "setExactAndAllowWhileIdle", "cancel", + "cancelAll", + "setTime", + "setTimeZone", + "canScheduleExactAlarms", + "getNextAlarmClock", ) specs.addAll( - "android/app/KeyguardManager", - "KeyguardManager", - "isKeyguardLocked", - "isDeviceLocked", - "isKeyguardSecure", + "android/app/blob/BlobStoreManager", + "BlobStoreManager", + "createSession", + "openSession", + "abandonSession", + "openBlob", + "acquireLease", + "releaseLease", + "getRemainingLeaseQuotaBytes", + "getLeasedBlobs", + ) + specs.addAll( + "android/app/blob/BlobStoreManager\$Session", + "BlobStoreManager.Session", + "openWrite", + "openRead", + "getSize", + "close", + "abandon", + "allowPackageAccess", + "isPackageAccessAllowed", + "allowSameSignatureAccess", + "isSameSignatureAccessAllowed", + "allowPublicAccess", + "isPublicAccessAllowed", + "commit", ) + // endregion + + // region accounts, credentials & permissions + specs.addAll( "android/accounts/AccountManager", "AccountManager", "getAccounts", + "getPassword", + "getAuthenticatorTypes", + "getAccountsByTypeForPackage", "getAccountsByType", + "hasFeatures", + "getAccountsByTypeAndFeatures", + "addAccountExplicitly", + "getPackagesAndVisibilityForAccount", + "getAccountsAndVisibilityForPackage", + "setAccountVisibility", + "getAccountVisibility", + "notifyAccountAuthenticated", + "renameAccount", + "getPreviousName", + "removeAccount", + "removeAccountExplicitly", + "invalidateAuthToken", + "peekAuthToken", + "setPassword", + "clearPassword", + "setUserData", + "setAuthToken", + "blockingGetAuthToken", "getAuthToken", - ) - - specs.addAll("android/os/UserManager", "UserManager", "getUserProfiles", "isUserUnlocked") - - specs.addAll( - "android/hardware/display/DisplayManager", - "DisplayManager", - "getDisplays", - "getDisplay", - ) - - specs.addAll( - "android/os/Vibrator", - "Vibrator", - "vibrate", - "cancel", - "hasVibrator", - "hasAmplitudeControl", - ) - - specs.addAll( - "android/os/VibratorManager", - "VibratorManager", - "getVibratorIds", - "getDefaultVibrator", - "vibrate", - "cancel", - ) - - specs.addAll( - "android/app/job/JobScheduler", - "JobScheduler", - "schedule", - "enqueue", - "cancel", - "cancelAll", - "getAllPendingJobs", - "getPendingJob", + "addAccount", + "confirmCredentials", + "updateCredentials", + "editProperties", + "getAuthTokenByFeatures", + "addOnAccountsUpdatedListener", + "removeOnAccountsUpdatedListener", + "startAddAccountSession", + "startUpdateCredentialsSession", + "finishSession", + "isCredentialsUpdateSuggested", ) specs.addAll( - "android/content/pm/ShortcutManager", - "ShortcutManager", - "setDynamicShortcuts", - "addDynamicShortcuts", - "removeDynamicShortcuts", - "removeAllDynamicShortcuts", - "getDynamicShortcuts", - "getPinnedShortcuts", - "updateShortcuts", - "requestPinShortcut", - "isRequestPinShortcutSupported", - "pushDynamicShortcut", + "android/credentials/CredentialManager", + "CredentialManager", + "getCredential", + "prepareGetCredential", + "createCredential", + "clearCredentialState", + "isEnabledCredentialProviderService", + "registerCredentialDescription", + "unregisterCredentialDescription", ) specs.addAll( "android/app/AppOpsManager", "AppOpsManager", "checkOp", + "unsafeCheckOp", "checkOpNoThrow", + "unsafeCheckOpNoThrow", + "checkOpRawNoThrow", + "unsafeCheckOpRaw", + "unsafeCheckOpRawNoThrow", "noteOp", "noteOpNoThrow", "noteProxyOp", "noteProxyOpNoThrow", "startOp", "startOpNoThrow", + "startProxyOp", + "startProxyOpNoThrow", "finishOp", + "finishProxyOp", + "isOpActive", "checkPackage", + "startWatchingMode", + "stopWatchingMode", + "startWatchingActive", + "stopWatchingActive", + "setOnOpNotedCallback", ) + specs.addAll("android/app/role/RoleManager", "RoleManager", "isRoleAvailable", "isRoleHeld") + + // endregion + + // region device policy & enterprise + specs.addAll( - "android/os/storage/StorageManager", - "StorageManager", - "getStorageVolumes", - "getPrimaryStorageVolume", - "getAllocatableBytes", - "getCacheSizeBytes", + "android/app/admin/DevicePolicyManager", + "DevicePolicyManager", + "acknowledgeDeviceCompliant", + "addCrossProfileIntentFilter", + "addCrossProfileWidgetProvider", + "addOverrideApn", + "addPersistentPreferredActivity", + "addUserRestriction", + "addUserRestrictionGlobally", + "bindDeviceAdminServiceAsUser", + "canAdminGrantSensorsPermissions", + "canUsbDataSignalingBeDisabled", + "clearApplicationUserData", + "clearCrossProfileIntentFilters", + "clearDeviceOwnerApp", + "clearPackagePersistentPreferredActivities", + "clearProfileOwner", + "clearResetPasswordToken", + "clearUserRestriction", + "createAdminSupportIntent", + "enableSystemApp", + "generateKeyPair", + "getApplicationRestrictionsManagingPackage", + "getAutoTimeEnabled", + "getAutoTimeRequired", + "getAutoTimeZoneEnabled", + "getBluetoothContactSharingDisabled", + "getCameraDisabled", + "getCrossProfileCallerIdDisabled", + "getCrossProfileContactsSearchDisabled", + "getCurrentFailedPasswordAttempts", + "getDeviceOwnerComponentOnAnyUser", + "getDeviceOwnerLockScreenInfo", + "getDeviceOwnerNameOnAnyUser", + "getEndUserSessionMessage", + "getGlobalPrivateDnsMode", + "getManagedProfileMaximumTimeOff", + "getManagedSubscriptionsPolicy", + "getMaximumFailedPasswordsForWipe", + "getMaximumTimeToLock", + "getOverrideApns", + "getPasswordComplexity", + "getPasswordExpiration", + "getPasswordExpirationTimeout", + "getPasswordHistoryLength", + "getPasswordMinimumLength", + "getPasswordMinimumLetters", + "getPasswordMinimumLowerCase", + "getPasswordMinimumNonLetter", + "getPasswordMinimumNumeric", + "getPasswordMinimumSymbols", + "getPasswordMinimumUpperCase", + "getPasswordQuality", + "getPermissionPolicy", + "getRequiredPasswordComplexity", + "getRequiredStrongAuthTimeout", + "getScreenCaptureDisabled", + "getSecondaryUsers", + "getShortSupportMessage", + "getStartUserSessionMessage", + "getStorageEncryption", + "getStorageEncryptionStatus", + "getSubscriptionIds", + "getTransferOwnershipBundle", + "getUserControlDisabledPackages", + "getUserProvisioningState", + "getWifiSsidPolicy", + "grantKeyPairToApp", + "grantKeyPairToWifiAuth", + "hasCaCertInstalled", + "hasGrantedPolicy", + "hasKeyPair", + "hasLockdownAdminConfiguredNetworks", + "installCaCert", + "installExistingPackage", + "installKeyPair", + "installSystemUpdate", + "isActivePasswordSufficient", + "isActivePasswordSufficientForDeviceRequirement", + "isAdminActive", + "isAffiliatedUser", + "isAlwaysOnVpnLockdownEnabled", + "isApplicationHidden", + "isBackupServiceEnabled", + "isCallerApplicationRestrictionsManagingPackage", + "isCommonCriteriaModeEnabled", + "isComplianceAcknowledgementRequired", + "isDeviceFinanced", + "isDeviceOwnerApp", + "isEphemeralUser", + "isFinancedDevice", + "isKeyPairGrantedToWifiAuth", + "isLockTaskPermitted", + "isLogoutEnabled", + "isManagedKiosk", + "isManagedProfile", + "isMasterVolumeMuted", + "isOverrideApnEnabled", + "isPackageAllowedToAccessCalendar", + "isPackageSuspended", + "isProfileOwnerApp", + "isProvisioningAllowed", + "isResetPasswordTokenActive", + "isSafeOperation", + "isSecurityLoggingEnabled", + "isStatusBarDisabled", + "isUnattendedManagedKiosk", + "isUninstallBlocked", + "isUsbDataSignalingEnabled", + "isUsingUnifiedPassword", + "listForegroundAffiliatedUsers", + "lockNow", + "provisionFullyManagedDevice", + "reboot", + "removeActiveAdmin", + "removeCrossProfileWidgetProvider", + "removeKeyPair", + "removeOverrideApn", + "removeUser", + "requestBugreport", + "resetPassword", + "resetPasswordWithToken", + "revokeKeyPairFromApp", + "revokeKeyPairFromWifiAuth", + "setAccountManagementDisabled", + "setActiveAdmin", + "setAffiliationIds", + "setAlwaysOnVpnPackage", + "setAppFunctionsPolicy", + "setApplicationHidden", + "setApplicationRestrictions", + "setApplicationRestrictionsManagingPackage", + "setAutoTimeEnabled", + "setAutoTimePolicy", + "setAutoTimeRequired", + "setAutoTimeZoneEnabled", + "setAutoTimeZonePolicy", + "setBackupServiceEnabled", + "setBluetoothContactSharingDisabled", + "setCameraDisabled", + "setCertInstallerPackage", + "setCommonCriteriaModeEnabled", + "setConfiguredNetworksLockdownState", + "setContentProtectionPolicy", + "setCredentialManagerPolicy", + "setCrossProfileCalendarPackages", + "setCrossProfileCallerIdDisabled", + "setCrossProfileContactsSearchDisabled", + "setCrossProfilePackages", + "setDefaultDialerApplication", + "setDefaultSmsApplication", + "setDeviceOwnerLockScreenInfo", + "setEndUserSessionMessage", + "setFactoryResetProtectionPolicy", + "setGlobalSetting", + "setKeepUninstalledPackages", + "setKeyPairCertificate", + "setKeyguardDisabled", + "setKeyguardDisabledFeatures", + "setLocationEnabled", + "setLockTaskFeatures", + "setLockTaskPackages", + "setLogoutEnabled", + "setLongSupportMessage", + "setManagedProfileCallerIdAccessPolicy", + "setManagedProfileContactsAccessPolicy", + "setManagedProfileMaximumTimeOff", + "setManagedSubscriptionsPolicy", + "setMasterVolumeMuted", + "setMaximumFailedPasswordsForWipe", + "setMaximumTimeToLock", + "setMinimumRequiredWifiSecurityLevel", + "setMtePolicy", + "setNearbyAppStreamingPolicy", + "setNearbyNotificationStreamingPolicy", + "setNetworkLoggingEnabled", + "setOrganizationColor", + "setOrganizationId", + "setOrganizationName", + "setOverrideApnsEnabled", + "setPackagesSuspended", + "setPasswordExpirationTimeout", + "setPasswordHistoryLength", + "setPasswordMinimumLength", + "setPasswordMinimumLetters", + "setPasswordMinimumLowerCase", + "setPasswordMinimumNonLetter", + "setPasswordMinimumNumeric", + "setPasswordMinimumSymbols", + "setPasswordMinimumUpperCase", + "setPasswordQuality", + "setPermissionGrantState", + "setPermissionPolicy", + "setPermittedAccessibilityServices", + "setPermittedCrossProfileNotificationListeners", + "setPermittedInputMethods", + "setPersonalAppsSuspended", + "setPreferentialNetworkServiceConfigs", + "setPreferentialNetworkServiceEnabled", + "setProfileEnabled", + "setProfileName", + "setProfileOwnerCanAccessDeviceIds", + "setProfileOwnerOnOrganizationOwnedDevice", + "setRecommendedGlobalProxy", + "setRequiredPasswordComplexity", + "setRequiredStrongAuthTimeout", + "setResetPasswordToken", + "setRestrictionsProvider", + "setScreenCaptureDisabled", + "setSecureSetting", + "setSecurityLoggingEnabled", + "setShortSupportMessage", + "setStartUserSessionMessage", + "setStatusBarDisabled", + "setStorageEncryption", + "setSystemSetting", + "setSystemUpdatePolicy", + "setTime", + "setTimeZone", + "setTrustAgentConfiguration", + "setUninstallBlocked", + "setUsbDataSignalingEnabled", + "setUserControlDisabledPackages", + "setUserIcon", + "setWifiSsidPolicy", + "switchUser", + "transferOwnership", + "uninstallAllUserCaCerts", + "uninstallCaCert", + "updateOverrideApn", + "wipeData", + "wipeDevice", ) + // endregion + + // region UI, launcher & companion + specs.addAll( - "android/telephony/SubscriptionManager", - "SubscriptionManager", - "getActiveSubscriptionInfoList", - "getActiveSubscriptionInfo", - "getActiveSubscriptionInfoCount", - "getActiveSubscriptionInfoForSimSlotIndex", + "android/app/KeyguardManager", + "KeyguardManager", + "isKeyguardLocked", + "isKeyguardSecure", + "inKeyguardRestrictedInputMode", + "isDeviceLocked", + "isDeviceSecure", + "requestDismissKeyguard", + "exitKeyguardSecurely", + "addKeyguardLockedStateListener", + "removeKeyguardLockedStateListener", + "addDeviceLockedStateListener", + "removeDeviceLockedStateListener", + ) + specs.addAll( + "android/app/KeyguardManager\$KeyguardLock", + "KeyguardManager.KeyguardLock", + "disableKeyguard", + "reenableKeyguard", ) specs.addAll( - "android/app/WallpaperManager", - "WallpaperManager", - "getDrawable", - "peekDrawable", - "getWallpaperColors", + "android/app/SearchManager", + "SearchManager", + "getGlobalSearchActivity", + "getSearchableInfo", + "getSearchablesInGlobalSearch", + ) + + specs.addAll( + "android/app/UiModeManager", + "UiModeManager", + "enableCarMode", + "disableCarMode", + "getCurrentModeType", + "setNightMode", + "setApplicationNightMode", + "getNightMode", + "getCustomNightModeStart", + "setCustomNightModeStart", + "getCustomNightModeEnd", + "setCustomNightModeEnd", + "getContrast", + "addContrastChangeListener", + "removeContrastChangeListener", + ) + + specs.addAll("android/app/GameManager", "GameManager", "getGameMode", "setGameState") + + specs.addAll( + "android/app/LocaleManager", + "LocaleManager", + "setApplicationLocales", + "getApplicationLocales", + "getSystemLocales", + "setOverrideLocaleConfig", + "getOverrideLocaleConfig", + ) + + specs.addAll( + "android/app/GrammaticalInflectionManager", + "GrammaticalInflectionManager", + "setRequestedApplicationGrammaticalGender", + "getSystemGrammaticalGender", + ) + + specs.addAll( + "android/app/StatusBarManager", + "StatusBarManager", + "requestAddTileService", + "canLaunchCaptureContentActivityForNote", + ) + + specs.addAll( + "android/appwidget/AppWidgetManager", + "AppWidgetManager", + "updateAppWidget", + "updateAppWidgetOptions", + "getAppWidgetOptions", + "partiallyUpdateAppWidget", + "updateAppWidgetProviderInfo", + "notifyAppWidgetViewDataChanged", + "getInstalledProvidersForProfile", + "getInstalledProvidersForPackage", + "getInstalledProviders", + "getAppWidgetInfo", + "bindAppWidgetIdIfAllowed", + "getAppWidgetIds", + "isRequestPinAppWidgetSupported", + "requestPinAppWidget", + "setWidgetPreview", + "getWidgetPreview", + "removeWidgetPreview", + ) + + specs.addAll( + "android/companion/CompanionDeviceManager", + "CompanionDeviceManager", + "associate", + "buildAssociationCancellationIntent", + "enableSystemDataSyncForTypes", + "disableSystemDataSyncForTypes", + "getMyAssociations", + "disassociate", + "requestNotificationAccess", + "hasNotificationAccess", + "removeBond", + "startObservingDevicePresence", + "stopObservingDevicePresence", + "attachSystemDataTransport", + "detachSystemDataTransport", + "buildPermissionTransferUserConsentIntent", + "isPermissionTransferUserConsented", + "startSystemDataTransfer", + "setDeviceId", + ) + + specs.addAll( + "android/companion/virtual/VirtualDeviceManager", + "VirtualDeviceManager", + "getVirtualDevices", + "getVirtualDevice", + "registerVirtualDeviceListener", + "unregisterVirtualDeviceListener", + ) + + specs.addAll( + "android/app/WallpaperManager", + "WallpaperManager", + "getDrawable", + "getBuiltInDrawable", + "peekDrawable", + "getFastDrawable", + "peekFastDrawable", + "getWallpaperFile", + "addOnColorsChangedListener", + "removeOnColorsChangedListener", + "getWallpaperColors", + "getWallpaperInfo", + "getWallpaperId", "setResource", "setBitmap", + "setStream", + "hasResourceWallpaper", "getDesiredMinimumWidth", "getDesiredMinimumHeight", + "suggestDesiredDimensions", + "setDisplayPadding", + "clearWallpaper", + "setWallpaperOffsets", + "sendWallpaperCommand", "isWallpaperSupported", "isSetWallpaperAllowed", + "clearWallpaperOffsets", + "clear", + ) + + // endregion + + // region connectivity + + specs.addAll( + "android/net/ConnectivityManager", + "ConnectivityManager", + "getActiveNetworkInfo", + "getActiveNetwork", + "getNetworkInfo", + "getAllNetworkInfo", + "getAllNetworks", + "getLinkProperties", + "getNetworkCapabilities", + "startUsingNetworkFeature", + "stopUsingNetworkFeature", + "requestRouteToHost", + "addDefaultNetworkActiveListener", + "removeDefaultNetworkActiveListener", + "isDefaultNetworkActive", + "reportBadNetwork", + "reportNetworkConnectivity", + "getDefaultProxy", + "isActiveNetworkMetered", + "requestNetwork", + "reserveNetwork", + "releaseNetworkRequest", + "registerNetworkCallback", + "registerDefaultNetworkCallback", + "registerBestMatchingNetworkCallback", + "requestBandwidthUpdate", + "unregisterNetworkCallback", + "getMultipathPreference", + "bindProcessToNetwork", + "setProcessDefaultNetwork", + "getRestrictBackgroundStatus", + "getNetworkWatchlistConfigHash", + "getConnectionOwnerUid", + ) + + specs.addAll( + "android/app/usage/NetworkStatsManager", + "NetworkStatsManager", + "querySummaryForDevice", + "querySummaryForUser", + "querySummary", + "queryDetailsForUid", + "queryDetailsForUidTag", + "queryDetailsForUidTagState", + "queryDetails", + "registerUsageCallback", + "unregisterUsageCallback", + ) + + specs.addAll( + "android/net/nsd/NsdManager", + "NsdManager", + "registerService", + "unregisterService", + "discoverServices", + "stopServiceDiscovery", + "resolveService", + "stopServiceResolution", + "registerServiceInfoCallback", + "unregisterServiceInfoCallback", + ) + + specs.addAll( + "android/net/wifi/WifiManager", + "WifiManager", + "getConfiguredNetworks", + "getCallerConfiguredNetworks", + "addNetworkPrivileged", + "addNetwork", + "updateNetwork", + "addOrUpdatePasspointConfiguration", + "removePasspointConfiguration", + "getPasspointConfigurations", + "removeNetwork", + "removeNonCallerConfiguredNetworks", + "enableNetwork", + "disableNetwork", + "disconnect", + "reconnect", + "reassociate", + "addNetworkSuggestions", + "removeNetworkSuggestions", + "getNetworkSuggestions", + "isPreferredNetworkOffloadSupported", + "is24GHzBandSupported", + "is5GHzBandSupported", + "is60GHzBandSupported", + "is6GHzBandSupported", + "isWifiStandardSupported", + "getConnectionInfo", + "isScanAlwaysAvailable", + "getChannelData", + "startScan", + "getScanResults", + "getDhcpInfo", + "setWifiEnabled", + "isWifiEnabled", + "pingSupplicant", + "registerSubsystemRestartTrackingCallback", + "unregisterSubsystemRestartTrackingCallback", + "getWifiState", + "addWifiStateChangedListener", + "removeWifiStateChangedListener", + "calculateSignalLevel", + "validateSoftApConfiguration", + "startLocalOnlyHotspot", + "setTdlsEnabled", + "setTdlsEnabledWithMacAddress", + "isTdlsOperationCurrentlyAvailable", + "getMaxSupportedConcurrentTdlsSessions", + "getNumberOfEnabledTdlsSessions", + "getWifiApConfiguration", + "setWifiApConfiguration", + "allowAutojoinGlobal", + "queryAutojoinGlobal", + "registerScanResultsCallback", + "unregisterScanResultsCallback", + "addSuggestionConnectionStatusListener", + "removeSuggestionConnectionStatusListener", + "addLocalOnlyConnectionFailureListener", + "removeLocalOnlyConnectionFailureListener", + "isScanThrottleEnabled", + "isAutoWakeupEnabled", + "isCarrierNetworkOffloadEnabled", + "addSuggestionUserApprovalStatusListener", + "removeSuggestionUserApprovalStatusListener", + "flushPasspointAnqpCache", + "getAllowedChannels", + "getUsableChannels", + "isWifiPasspointEnabled", + "reportCreateInterfaceImpact", + "getMaxNumberOfChannelsPerNetworkSpecifierRequest", + "setSendDhcpHostnameRestriction", + "querySendDhcpHostnameRestriction", + "setPerSsidRoamingMode", + "removePerSsidRoamingMode", + "getPerSsidRoamingModes", + "disallowCurrentSuggestedNetwork", + ) + specs.addAll( + "android/net/wifi/WifiManager\$WifiLock", + "WifiManager.WifiLock", + "acquire", + "release", + "setWorkSource", + ) + specs.addAll( + "android/net/wifi/WifiManager\$MulticastLock", + "WifiManager.MulticastLock", + "acquire", + "release", + ) + + specs.addAll( + "android/net/wifi/p2p/WifiP2pManager", + "WifiP2pManager", + "initialize", + "setWfdInfo", + "removeClient", + "isSetVendorElementsSupported", + "isChannelConstrainedDiscoverySupported", + "isGroupClientRemovalSupported", + "isGroupOwnerIPv6LinkLocalAddressProvided", + "isWiFiDirectR2Supported", + "isPccModeSupported", + "registerWifiP2pListener", + "unregisterWifiP2pListener", + ) + + specs.addAll( + "android/nfc/NfcAdapter", + "NfcAdapter", + "isEnabled", + "enable", + "disable", + "isReaderModeAnnotationSupported", + "isObserveModeSupported", + "isObserveModeEnabled", + "setObserveModeEnabled", + "enableForegroundDispatch", + "disableForegroundDispatch", + "setDiscoveryTechnology", + "isSecureNfcSupported", + "getNfcAntennaInfo", + "isSecureNfcEnabled", + "isReaderOptionSupported", + "isReaderOptionEnabled", + "ignore", + "isWlcEnabled", + "getWlcListenerDeviceInfo", + "isTagIntentAppPreferenceSupported", + "isTagIntentAllowed", + ) + + specs.addAll( + "android/net/VpnService", + "VpnService", + "prepare", + "setUnderlyingNetworks", + "isAlwaysOn", + "isLockdownEnabled", + ) + + specs.addAll( + "android/net/wifi/aware/WifiAwareManager", + "WifiAwareManager", + "isAvailable", + "isDeviceAttached", + "isSetChannelOnDataPathSupported", + "isInstantCommunicationModeEnabled", + "getCharacteristics", + "getAvailableAwareResources", + "attach", + "setOpportunisticModeEnabled", + "isOpportunisticModeEnabled", + "resetPairedDevices", + "removePairedDevice", + "getPairedDevices", + ) + + specs.addAll( + "android/net/wifi/rtt/WifiRttManager", + "WifiRttManager", + "isAvailable", + "startRanging", + "cancelRanging", + "getRttCharacteristics", + ) + + specs.addAll( + "android/net/ConnectivityDiagnosticsManager", + "ConnectivityDiagnosticsManager", + "registerConnectivityDiagnosticsCallback", + "unregisterConnectivityDiagnosticsCallback", + ) + + // endregion + + // region bluetooth + + specs.addAll( + "android/bluetooth/BluetoothAdapter", + "BluetoothAdapter", + "isEnabled", + "getState", + "getName", + "getAddress", + "getBondedDevices", + "startDiscovery", + "cancelDiscovery", + "isDiscovering", + "enable", + "disable", + "getScanMode", + "setScanMode", + ) + + specs.addAll( + "android/bluetooth/BluetoothDevice", + "BluetoothDevice", + "getName", + "getBondState", + "getType", + "createBond", + "removeBond", + "connectGatt", + "getBatteryLevel", + "getUuids", + ) + + specs.addAll( + "android/bluetooth/BluetoothGatt", + "BluetoothGatt", + "connect", + "disconnect", + "discoverServices", + "readCharacteristic", + "writeCharacteristic", + "readDescriptor", + "writeDescriptor", + "readRemoteRssi", + "requestMtu", + ) + + specs.addAll( + "android/bluetooth/BluetoothManager", + "BluetoothManager", + "getConnectedDevices", + "getConnectionState", + "getDevicesMatchingConnectionStates", + "openGattServer", + ) + + // endregion + + // region telephony + + specs.addAll( + "android/telephony/TelephonyManager", + "TelephonyManager", + "getDeviceSoftwareVersion", + "getDeviceId", + "getImei", + "getTypeAllocationCode", + "getMeid", + "getManufacturerCode", + "getNai", + "getCellLocation", + "getNeighboringCellInfo", + "getPhoneType", + "getNetworkOperator", + "getNetworkOperatorName", + "getNetworkCountryIso", + "getNetworkType", + "getDataNetworkType", + "getVoiceNetworkType", + "getSimOperator", + "getSimOperatorName", + "hasIccCard", + "getSimState", + "getSimSerialNumber", + "getCardIdForDefaultEuicc", + "getUiccCardsInfo", + "getSubscriberId", + "uploadCallComposerPicture", + "getGroupIdLevel1", + "getLine1Number", + "setLine1NumberForDisplay", + "getVoiceMailNumber", + "setVoiceMailNumber", + "getVisualVoicemailPackageName", + "setVisualVoicemailSmsFilterSettings", + "sendVisualVoicemailSms", + "getVoiceMailAlphaTag", + "sendDialerSpecialCode", + "getCallState", + "getCallStateForSubscription", + "getDataActivity", + "getDataState", + "listen", + "getAllCellInfo", + "requestCellInfoUpdate", + "getMmsUserAgent", + "getMmsUAProfUrl", + "iccOpenLogicalChannel", + "iccCloseLogicalChannel", + "iccTransmitApduLogicalChannel", + "iccTransmitApduBasicChannel", + "iccExchangeSimIO", + "sendEnvelopeWithStatus", + "rebootModem", + "getIccAuthentication", + "getForbiddenPlmns", + "setForbiddenPlmns", + "setNetworkSelectionModeAutomatic", + "requestNetworkScan", + "setNetworkSelectionModeManual", + "getNetworkSelectionMode", + "getManualNetworkSelectionPlmn", + "setAllowedNetworkTypesForReason", + "getAllowedNetworkTypesForReason", + "setPreferredNetworkTypeToGlobal", + "hasCarrierPrivileges", + "setOperatorBrandOverride", + "setCallComposerStatus", + "getCallComposerStatus", + "sendUssdRequest", + "isConcurrentVoiceAndDataSupported", + "setDataEnabled", + "isDataEnabled", + "isDataRoamingEnabled", + "canChangeDtmfToneLength", + "isWorldPhone", + "isRttSupported", + "isHearingAidCompatibilitySupported", + "getPhoneAccountHandle", + "getServiceState", + "getVoicemailRingtoneUri", + "setVoicemailRingtoneUri", + "isVoicemailVibrationEnabled", + "setVoicemailVibrationEnabled", + "getSimCarrierId", + "getSimCarrierIdName", + "getSimSpecificCarrierId", + "getSimSpecificCarrierIdName", + "getCarrierIdFromSimMccMnc", + "getCarrierRestrictionStatus", + "setDataEnabledForReason", + "isDataEnabledForReason", + "isManualNetworkSelectionAllowed", + "getSignalStrength", + "isDataConnectionAllowed", + "getSupportedRadioAccessFamily", + "getEmergencyNumberList", + "isEmergencyNumber", + "setPreferredOpportunisticDataSubscription", + "getPreferredOpportunisticDataSubscription", + "updateAvailableNetworks", + "isModemEnabledForSlot", + "isMultiSimSupported", + "switchMultiSimConfig", + "doesSwitchMultiSimConfigTriggerReboot", + "getEquivalentHomePlmns", + "isRadioInterfaceCapabilitySupported", + "registerTelephonyCallback", + "unregisterTelephonyCallback", + "setSignalStrengthUpdateRequest", + "clearSignalStrengthUpdateRequest", + "getNetworkSlicingConfiguration", + "isPremiumCapabilityAvailableForPurchase", + "purchasePremiumCapability", + "getPrimaryImei", + ) + + specs.addAll( + "android/telephony/SubscriptionManager", + "SubscriptionManager", + "getActiveSubscriptionInfo", + "getActiveSubscriptionInfoForSimSlotIndex", + "getAllSubscriptionInfoList", + "getActiveSubscriptionInfoList", + "getAccessibleSubscriptionInfoList", + "getActiveSubscriptionInfoCount", + "getActiveSubscriptionInfoCountMax", + "getSlotIndex", + "getSubscriptionId", + "getSubscriptionIds", + "getDefaultSubscriptionId", + "getDefaultVoiceSubscriptionId", + "getDefaultSmsSubscriptionId", + "getDefaultDataSubscriptionId", + "isNetworkRoaming", + "getOpportunisticSubscriptions", + "setOpportunistic", + "createSubscriptionGroup", + "addSubscriptionsIntoGroup", + "removeSubscriptionsFromGroup", + "getSubscriptionsInGroup", + "setDeviceToDeviceStatusSharingPreference", + "setDeviceToDeviceStatusSharingContacts", + "getActiveDataSubscriptionId", + "getPhoneNumber", + "setCarrierPhoneNumber", + "isSubscriptionAssociatedWithUser", + ) + + specs.addAll( + "android/telephony/CarrierConfigManager", + "CarrierConfigManager", + "getConfigForSubId", + "getConfig", + "notifyConfigChangedForSubId", + "getConfigByComponentForSubId", + ) + + specs.addAll( + "android/telecom/TelecomManager", + "TelecomManager", + "getDefaultOutgoingPhoneAccount", + "getUserSelectedOutgoingPhoneAccount", + "getSimCallManager", + "getSimCallManagerForSubscription", + "getCallCapablePhoneAccounts", + "getCallCapablePhoneAccountsAcrossProfiles", + "getSelfManagedPhoneAccounts", + "getOwnSelfManagedPhoneAccounts", + "getRegisteredPhoneAccounts", + "getPhoneAccount", + "registerPhoneAccount", + "unregisterPhoneAccount", + "getDefaultDialerPackage", + "getSystemDialerPackage", + "isVoiceMailNumber", + "getVoiceMailNumber", + "getLine1Number", + "isInCall", + "hasManageOngoingCallsPermission", + "isInManagedCall", + "endCall", + "acceptRingingCall", + "silenceRinger", + "isTtySupported", + "addNewIncomingCall", + "addNewIncomingConference", + "handleMmi", + "getAdnUriForPhoneAccount", + "cancelMissedCallsNotification", + "showInCallScreen", + "placeCall", + "startConference", + "createManageBlockedNumbersIntent", + "isIncomingCallPermitted", + "isOutgoingCallPermitted", + "acceptHandover", + "addCall", + ) + + specs.addAll( + "android/telephony/SmsManager", + "SmsManager", + "sendTextMessage", + "sendTextMessageWithoutPersisting", + "injectSmsPdu", + "sendMultipartTextMessage", + "sendDataMessage", + "getSubscriptionId", + "getDefaultSmsSubscriptionId", + "getSmsCapacityOnIcc", + "sendMultimediaMessage", + "downloadMultimediaMessage", + "getCarrierConfigValues", + "createAppSpecificSmsToken", + "createAppSpecificSmsTokenWithPackageInfo", + "getSmscAddress", + "setSmscAddress", + ) + + specs.addAll( + "android/telephony/euicc/EuiccManager", + "EuiccManager", + "isEnabled", + "getEid", + "getAvailableMemoryInBytes", + "downloadSubscription", + "continueOperation", + "getEuiccInfo", + "deleteSubscription", + "switchToSubscription", + "updateSubscriptionNickname", + "isSimPortAvailable", + ) + + // endregion + + // region hardware + + specs.addAll( + "android/hardware/camera2/CameraManager", + "CameraManager", + "getCameraIdList", + "getCameraCharacteristics", + "openCamera", + ) + + specs.addAll( + "android/hardware/ConsumerIrManager", + "ConsumerIrManager", + "hasIrEmitter", + "transmit", + "getCarrierFrequencies", + ) + + specs.addAll( + "android/hardware/biometrics/BiometricManager", + "BiometricManager", + "canAuthenticate", + "getLastAuthenticationTime", + ) + specs.addAll( + "android/hardware/biometrics/BiometricManager\$Strings", + "BiometricManager.Strings", + "getButtonLabel", + "getPromptMessage", + "getSettingName", + ) + + specs.addAll( + "android/hardware/fingerprint/FingerprintManager", + "FingerprintManager", + "authenticate", + "hasEnrolledFingerprints", + "isHardwareDetected", + ) + + specs.addAll( + "android/hardware/usb/UsbManager", + "UsbManager", + "getDeviceList", + "openDevice", + "getAccessoryList", + "openAccessory", + "hasPermission", + "requestPermission", + ) + + specs.addAll( + "android/hardware/display/DisplayManager", + "DisplayManager", + "getDisplays", + "getDisplay", + ) + + specs.addAll( + "android/hardware/input/InputManager", + "InputManager", + "getInputDevice", + "getInputDeviceViewBehavior", + "getInputDeviceIds", + "registerInputDeviceListener", + "unregisterInputDeviceListener", + "verifyInputEvent", + "getHostUsiVersion", + ) + + // endregion + + // region os & power + + specs.addAll( + "android/os/PowerManager", + "PowerManager", + "goToSleep", + "wakeUp", + "isWakeLockLevelSupported", + "isInteractive", + "reboot", + "isPowerSaveMode", + "getBatteryDischargePrediction", + "isBatteryDischargePredictionPersonalized", + "getLocationPowerSaveMode", + "isDeviceIdleMode", + "isDeviceLightIdleMode", + "isLowPowerStandbyEnabled", + "isExemptFromLowPowerStandby", + "isAllowedInLowPowerStandby", + "getCurrentThermalStatus", + "addThermalStatusListener", + "removeThermalStatusListener", + "addThermalHeadroomListener", + "removeThermalHeadroomListener", + "getThermalHeadroom", + "getThermalHeadroomThresholds", + ) + specs.addAll( + "android/os/PowerManager\$WakeLock", + "PowerManager.WakeLock", + "acquire", + "release", + "setWorkSource", + "setStateListener", + ) + + specs.addAll( + "android/os/UserManager", + "UserManager", + "getUserProfiles", + "isUserUnlocked", + "isAdminUser", + "isForegroundUserAdmin", + "isDemoUser", + "isUserForeground", + "isProfile", + "getUserName", + "getUserRestrictions", + "setUserRestriction", + "isQuietModeEnabled", + "getApplicationRestrictions", + "getUserCreationTime", + "getSerialNumberForUser", + "getUserForSerialNumber", + "getUserCount", + ) + + specs.addAll( + "android/os/storage/StorageManager", + "StorageManager", + "getStorageVolumes", + "getPrimaryStorageVolume", + "getAllocatableBytes", + "getCacheSizeBytes", + "registerStorageVolumeCallback", + "unregisterStorageVolumeCallback", + "mountObb", + "getManageSpaceActivityIntent", + "unmountObb", + "isObbMounted", + "getMountedObbPath", + "getUuidForPath", + "getCacheQuotaBytes", + "isCheckpointSupported", + ) + + specs.addAll( + "android/os/BatteryManager", + "BatteryManager", + "isCharging", + "getIntProperty", + "getLongProperty", + "getStringProperty", + ) + + specs.addAll( + "android/os/DropBoxManager", + "DropBoxManager", + "addData", + "addFile", + "isTagEnabled", + ) + + specs.addAll( + "android/os/health/SystemHealthManager", + "SystemHealthManager", + "takeUidSnapshot", + "takeUidSnapshots", + ) + + specs.addAll( + "android/os/Vibrator", + "Vibrator", + "vibrate", + "cancel", + "hasVibrator", + "hasAmplitudeControl", + ) + + specs.addAll( + "android/os/VibratorManager", + "VibratorManager", + "getVibratorIds", + "getDefaultVibrator", + "vibrate", + "cancel", ) val strictMode = "android/os/StrictMode" @@ -408,6 +1678,412 @@ object BinderMethodRegistry { specs.add(BinderMethodSpec(strictMode, method, "StrictMode", isStatic = true)) } + // endregion + + // region accessibility, input & text + + specs.addAll( + "android/view/accessibility/AccessibilityManager", + "AccessibilityManager", + "sendAccessibilityEvent", + "interrupt", + "getAccessibilityServiceList", + "getInstalledAccessibilityServiceList", + "getEnabledAccessibilityServiceList", + ) + + specs.addAll( + "android/view/autofill/AutofillManager", + "AutofillManager", + "requestAutofill", + "notifyViewEntered", + "notifyVirtualViewsReady", + "notifyViewExited", + "notifyViewVisibilityChanged", + "notifyValueChanged", + "notifyViewClicked", + "commit", + "cancel", + "disableAutofillServices", + "hasEnabledAutofillServices", + "getAutofillServiceComponentName", + "setUserData", + "isFieldClassificationEnabled", + "getDefaultFieldClassificationAlgorithm", + "getAvailableFieldClassificationAlgorithms", + "isAutofillSupported", + "registerCallback", + "unregisterCallback", + "showAutofillDialog", + ) + + specs.addAll( + "android/view/textservice/TextServicesManager", + "TextServicesManager", + "newSpellCheckerSession", + "getEnabledSpellCheckerInfos", + "getCurrentSpellCheckerInfo", + "isSpellCheckerEnabled", + ) + + specs.addAll( + "android/view/inputmethod/InputMethodManager", + "InputMethodManager", + "isActive", + "getInputMethodList", + "isStylusHandwritingAvailable", + "isConnectionlessStylusHandwritingAvailable", + "getCurrentInputMethodInfo", + "getEnabledInputMethodList", + "getEnabledInputMethodSubtypeList", + "showStatusIcon", + "hideStatusIcon", + "displayCompletions", + "updateExtractedText", + "showSoftInput", + "hideSoftInputFromWindow", + "startStylusHandwriting", + "startConnectionlessStylusHandwriting", + "startConnectionlessStylusHandwritingForDelegation", + "prepareStylusHandwritingDelegation", + "acceptStylusHandwritingDelegation", + "toggleSoftInputFromWindow", + "toggleSoftInput", + "restartInput", + "updateSelection", + "viewClicked", + "updateCursor", + "updateCursorAnchorInfo", + "sendAppPrivateCommand", + "setInputMethod", + "setInputMethodAndSubtype", + "hideSoftInputFromInputMethod", + "showSoftInputFromInputMethod", + "showInputMethodPicker", + "getCurrentInputMethodSubtype", + "setCurrentInputMethodSubtype", + "switchToLastInputMethod", + "switchToNextInputMethod", + "shouldOfferSwitchingToNextInputMethod", + "setAdditionalInputMethodSubtypes", + "setExplicitlyEnabledInputMethodSubtypes", + "getLastInputMethodSubtype", + ) + + specs.addAll( + "android/print/PrintManager", + "PrintManager", + "getPrintJobs", + "print", + "isPrintServiceEnabled", + ) + + // endregion + + // region media & location + + specs.addAll( + "android/media/AudioManager", + "AudioManager", + "isVolumeFixed", + "adjustStreamVolume", + "adjustVolume", + "adjustSuggestedStreamVolume", + "getStreamMaxVolume", + "getStreamVolume", + "getRingerMode", + "setRingerMode", + "setStreamVolume", + "adjustVolumeGroupVolume", + "isVolumeGroupMuted", + "isStreamMute", + "shouldVibrate", + "getVibrateSetting", + "setVibrateSetting", + "requestAudioFocus", + "abandonAudioFocus", + "setAllowedCapturePolicy", + "getAllowedCapturePolicy", + "setSpeakerphoneOn", + "isSpeakerphoneOn", + "startBluetoothSco", + "stopBluetoothSco", + "setBluetoothScoOn", + "isBluetoothScoOn", + "isBluetoothA2dpOn", + "setMicrophoneMute", + "isMicrophoneMute", + "setMode", + "getMode", + "isCallScreeningModeSupported", + "isMusicActive", + "playSoundEffect", + "loadSoundEffects", + "unloadSoundEffects", + "registerAudioPlaybackCallback", + "unregisterAudioPlaybackCallback", + "getActivePlaybackConfigurations", + "registerAudioRecordingCallback", + "unregisterAudioRecordingCallback", + "getActiveRecordingConfigurations", + "getEncodedSurroundMode", + "isSurroundFormatEnabled", + "setCommunicationDevice", + "clearCommunicationDevice", + "getCommunicationDevice", + "getAvailableCommunicationDevices", + ) + + specs.addAll( + "android/media/MediaRouter\$RouteInfo", + "MediaRouter.RouteInfo", + "requestSetVolume", + "requestUpdateVolume", + "getVolumeMax", + ) + + specs.addAll( + "android/media/session/MediaSessionManager", + "MediaSessionManager", + "getActiveSessions", + "getMediaKeyEventSession", + "getMediaKeyEventSessionPackageName", + "addOnActiveSessionsChangedListener", + "removeOnActiveSessionsChangedListener", + "addOnSession2TokensChangedListener", + "removeOnSession2TokensChangedListener", + "isTrustedForMediaControl", + "addOnMediaKeyEventSessionChangedListener", + "removeOnMediaKeyEventSessionChangedListener", + ) + + specs.addAll( + "android/media/session/MediaSession", + "MediaSession", + "setSessionActivity", + "setMediaButtonReceiver", + "setMediaButtonBroadcastReceiver", + "setFlags", + "setPlaybackToLocal", + "setPlaybackToRemote", + "setActive", + "sendSessionEvent", + "release", + "setPlaybackState", + "setMetadata", + "setQueue", + "setQueueTitle", + "setRatingType", + "setExtras", + ) + + specs.addAll( + "android/media/session/MediaController", + "MediaController", + "dispatchMediaButtonEvent", + "getPlaybackState", + "getMetadata", + "getQueue", + "getQueueTitle", + "getExtras", + "getRatingType", + "getFlags", + "getPlaybackInfo", + "getSessionActivity", + "setVolumeTo", + "adjustVolume", + "registerCallback", + "unregisterCallback", + "sendCommand", + "getPackageName", + "getSessionInfo", + "getTag", + ) + specs.addAll( + "android/media/session/MediaController\$TransportControls", + "MediaController.TransportControls", + "prepare", + "prepareFromMediaId", + "prepareFromSearch", + "prepareFromUri", + "play", + "playFromMediaId", + "playFromSearch", + "playFromUri", + "skipToQueueItem", + "pause", + "stop", + "seekTo", + "fastForward", + "skipToNext", + "rewind", + "skipToPrevious", + "setRating", + "setPlaybackSpeed", + "sendCustomAction", + ) + + specs.addAll( + "android/media/MediaRouter2", + "MediaRouter2", + "requestScan", + "cancelScanRequest", + "registerRouteCallback", + "unregisterRouteCallback", + "showSystemOutputSwitcher", + "setRouteListingPreference", + "transferTo", + "stop", + "getControllers", + "setRouteVolume", + ) + specs.addAll( + "android/media/MediaRouter2\$RoutingController", + "MediaRouter2.RoutingController", + "selectRoute", + "deselectRoute", + "setVolume", + "release", + ) + + specs.addAll( + "android/media/Spatializer", + "Spatializer", + "isEnabled", + "isAvailable", + "isHeadTrackerAvailable", + "addOnHeadTrackerAvailableListener", + "removeOnHeadTrackerAvailableListener", + "getImmersiveAudioLevel", + "canBeSpatialized", + "getSpatializedChannelMasks", + "addOnSpatializerStateChangedListener", + "removeOnSpatializerStateChangedListener", + ) + + specs.addAll( + "android/media/midi/MidiManager", + "MidiManager", + "registerDeviceCallback", + "unregisterDeviceCallback", + "getDevices", + "getDevicesForTransport", + "openDevice", + "openBluetoothDevice", + ) + + specs.addAll( + "android/media/tv/TvInputManager", + "TvInputManager", + "getTvInputList", + "getTvInputInfo", + "updateTvInputInfo", + "isParentalControlsEnabled", + "isRatingBlocked", + "getBlockedRatings", + ) + + specs.addAll( + "android/location/LocationManager", + "LocationManager", + "getLastKnownLocation", + "getCurrentLocation", + "requestLocationUpdates", + "requestFlush", + "removeUpdates", + "hasProvider", + "getAllProviders", + "getProviders", + "getBestProvider", + "getProvider", + "getProviderProperties", + "sendExtraCommand", + "addTestProvider", + "removeTestProvider", + "setTestProviderLocation", + "setTestProviderEnabled", + "addProximityAlert", + "removeProximityAlert", + "getGnssCapabilities", + "getGnssYearOfHardware", + "getGnssHardwareModelName", + "getGnssAntennaInfos", + ) + + specs.addAll( + "android/location/Geocoder", + "Geocoder", + "isPresent", + "getFromLocation", + "getFromLocationName", + ) + + // endregion + + // region voice & speech + + specs.addAll( + "android/speech/tts/TextToSpeech", + "TextToSpeech", + "shutdown", + "speak", + "playEarcon", + "playSilentUtterance", + "playSilence", + "getFeatures", + "isSpeaking", + "stop", + "getDefaultLanguage", + "setLanguage", + "getLanguage", + "getAvailableLanguages", + "getVoices", + "setVoice", + "getVoice", + "getDefaultVoice", + "isLanguageAvailable", + "synthesizeToFile", + ) + + specs.addAll( + "android/speech/SpeechRecognizer", + "SpeechRecognizer", + "isRecognitionAvailable", + "setRecognitionListener", + "startListening", + "stopListening", + "cancel", + "checkRecognitionSupport", + "triggerModelDownload", + "destroy", + ) + + // endregion + + // region health + + specs.addAll( + "android/health/connect/HealthConnectManager", + "HealthConnectManager", + "insertRecords", + "aggregate", + "aggregateGroupByDuration", + "aggregateGroupByPeriod", + "deleteRecords", + "getChangeLogs", + "getChangeLogToken", + "readRecords", + "updateRecords", + "upsertMedicalResources", + "readMedicalResources", + "deleteMedicalResources", + "createMedicalDataSource", + "getMedicalDataSources", + "deleteMedicalDataSourceWithData", + ) + + // endregion + return specs.groupBy { it.owner } } From 6f571dc2ea4b9f0b9f47efc2db0d19ed883f66d3 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 8 Jun 2026 14:45:42 +0200 Subject: [PATCH 05/10] Rename, ipc -> binder --- .../android/gradle/AndroidComponentsConfig.kt | 4 ++-- ...{BinderIpcExtension.kt => BinderExtension.kt} | 2 +- .../TracingInstrumentationExtension.kt | 6 +++--- .../gradle/instrumentation/binder/BinderIpc.kt | 2 +- ...pcMethodVisitor.kt => BinderMethodVisitor.kt} | 15 ++++++++------- ...VisitorTest.kt => BinderMethodVisitorTest.kt} | 16 +++++++++------- 6 files changed, 24 insertions(+), 21 deletions(-) rename plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/{BinderIpcExtension.kt => BinderExtension.kt} (81%) rename plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/{BinderIpcMethodVisitor.kt => BinderMethodVisitor.kt} (86%) rename plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/{BinderIpcMethodVisitorTest.kt => BinderMethodVisitorTest.kt} (89%) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt index 31d4ee09c..ffd9de1ac 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt @@ -196,7 +196,7 @@ fun ApplicationAndroidComponentsExtension.configure( extension.includeSourceContext, extension.dexguardEnabled, extension.tracingInstrumentation.appStart.enabled, - extension.tracingInstrumentation.binderIpc.enabled, + extension.tracingInstrumentation.binder.enabled, ) /** * We have to register SentryModulesService as a build event listener, so it will not be @@ -230,7 +230,7 @@ fun ApplicationAndroidComponentsExtension.configure( extension.tracingInstrumentation.appStart.enabled ) params.binderIpcEnabled.setDisallowChanges( - extension.tracingInstrumentation.binderIpc.enabled + extension.tracingInstrumentation.binder.enabled ) params.tmpDir.set( project.layout.buildDirectory.dir("sentry-logs/instrumentation/${variant.name}") diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderIpcExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt similarity index 81% rename from plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderIpcExtension.kt rename to plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt index 3247f94db..30e1d1db0 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderIpcExtension.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt @@ -4,7 +4,7 @@ import javax.inject.Inject import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property -open class BinderIpcExtension @Inject constructor(objects: ObjectFactory) { +open class BinderExtension @Inject constructor(objects: ObjectFactory) { /** Enables or disables Binder IPC call instrumentation. Defaults to true. */ val enabled: Property = objects.property(Boolean::class.java).convention(true) } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt index 10c9de7a8..eb85ca6bc 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt @@ -72,10 +72,10 @@ open class TracingInstrumentationExtension @Inject constructor(objects: ObjectFa appStartExtensionAction.execute(appStart) } - val binderIpc: BinderIpcExtension = objects.newInstance(BinderIpcExtension::class.java) + val binder: BinderExtension = objects.newInstance(BinderExtension::class.java) - fun binderIpc(binderIpcAction: Action) { - binderIpcAction.execute(binderIpc) + fun binderIpc(binderAction: Action) { + binderAction.execute(binder) } } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt index a95bbc196..d53ad602d 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt @@ -23,7 +23,7 @@ class BinderIpc : ClassInstrumentable { apiVersion, originalVisitor, CLASSNAME, - listOf(BinderIpcMethodInstrumentable()), + listOf(BinderMethodInstrumentable()), parameters, ) } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitor.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt similarity index 86% rename from plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitor.kt rename to plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt index 024d1ec4c..50de14c4d 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitor.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt @@ -10,21 +10,22 @@ import org.objectweb.asm.Type import org.objectweb.asm.commons.GeneratorAdapter import org.objectweb.asm.commons.Method -class BinderIpcMethodInstrumentable : MethodInstrumentable { +class BinderMethodInstrumentable : MethodInstrumentable { override fun getVisitor( instrumentableContext: MethodContext, apiVersion: Int, originalVisitor: MethodVisitor, parameters: SpanAddingClassVisitorFactory.SpanAddingParameters, - ): MethodVisitor = BinderIpcMethodVisitor(apiVersion, originalVisitor, instrumentableContext) + ): MethodVisitor = BinderMethodVisitor(apiVersion, originalVisitor, instrumentableContext) override fun isInstrumentable(data: MethodContext): Boolean = true } -private const val SENTRY_IPC_TRACER = "io/sentry/android/core/SentryIpcTracer" +private const val SENTRY_BINDER_ADAPTER = + "io/sentry/android/core/internal/binder/SentryBinderAdapter" -class BinderIpcMethodVisitor( +class BinderMethodVisitor( apiVersion: Int, originalVisitor: MethodVisitor, instrumentableContext: MethodContext, @@ -79,7 +80,7 @@ class BinderIpcMethodVisitor( mv.visitLdcInsn(name) mv.visitMethodInsn( Opcodes.INVOKESTATIC, - SENTRY_IPC_TRACER, + SENTRY_BINDER_ADAPTER, "onCallStart", "(Ljava/lang/String;Ljava/lang/String;)I", false, @@ -105,13 +106,13 @@ class BinderIpcMethodVisitor( mv.visitLabel(tryEnd) loadLocal(cookieLocal) - mv.visitMethodInsn(Opcodes.INVOKESTATIC, SENTRY_IPC_TRACER, "onCallEnd", "(I)V", false) + mv.visitMethodInsn(Opcodes.INVOKESTATIC, SENTRY_BINDER_ADAPTER, "onCallEnd", "(I)V", false) mv.visitJumpInsn(Opcodes.GOTO, afterFinally) // catch-all handler: call onCallEnd then re-throw mv.visitLabel(catchHandler) loadLocal(cookieLocal) - mv.visitMethodInsn(Opcodes.INVOKESTATIC, SENTRY_IPC_TRACER, "onCallEnd", "(I)V", false) + mv.visitMethodInsn(Opcodes.INVOKESTATIC, SENTRY_BINDER_ADAPTER, "onCallEnd", "(I)V", false) mv.visitInsn(Opcodes.ATHROW) mv.visitLabel(afterFinally) diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitorTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitorTest.kt similarity index 89% rename from plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitorTest.kt rename to plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitorTest.kt index 72a30e381..dc6b403e4 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpcMethodVisitorTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitorTest.kt @@ -15,7 +15,7 @@ import org.objectweb.asm.Opcodes import org.objectweb.asm.util.Textifier import org.objectweb.asm.util.TraceClassVisitor -class BinderIpcMethodVisitorTest { +class BinderMethodVisitorTest { @get:Rule val tmpDir = TemporaryFolder() @@ -27,7 +27,7 @@ class BinderIpcMethodVisitorTest { Opcodes.ASM9, writer, "TestClass", - listOf(BinderIpcMethodInstrumentable()), + listOf(BinderMethodInstrumentable()), TestSpanAddingParameters(debugOutput = false, inMemoryDir = tmpDir.root), ) reader.accept(visitor, ClassReader.SKIP_FRAMES) @@ -66,11 +66,11 @@ class BinderIpcMethodVisitorTest { val text = disassemble(instrumented) assertTrue( - text.contains("io/sentry/android/core/SentryIpcTracer.onCallStart"), + text.contains("io/sentry/android/core/internal/binder/SentryBinderAdapter.onCallStart"), "onCallStart should be emitted:\n$text", ) assertTrue( - text.contains("io/sentry/android/core/SentryIpcTracer.onCallEnd"), + text.contains("io/sentry/android/core/internal/binder/SentryBinderAdapter.onCallEnd"), "onCallEnd should be emitted:\n$text", ) assertTrue( @@ -101,7 +101,9 @@ class BinderIpcMethodVisitorTest { val instrumented = instrument(bytes) val text = disassemble(instrumented) - assertTrue(text.contains("io/sentry/android/core/SentryIpcTracer.onCallStart")) + assertTrue( + text.contains("io/sentry/android/core/internal/binder/SentryBinderAdapter.onCallStart") + ) assertTrue(text.contains("LDC \"Settings.Secure\"")) } @@ -117,7 +119,7 @@ class BinderIpcMethodVisitorTest { val instrumented = instrument(bytes) val text = disassemble(instrumented) - assertFalse(text.contains("SentryIpcTracer"), "unknown calls must not be wrapped:\n$text") + assertFalse(text.contains("SentryBinderAdapter"), "unknown calls must not be wrapped:\n$text") } @Test @@ -139,6 +141,6 @@ class BinderIpcMethodVisitorTest { val instrumented = instrument(bytes) val text = disassemble(instrumented) - assertFalse(text.contains("SentryIpcTracer")) + assertFalse(text.contains("SentryBinderAdapter")) } } From ebdabcadd547acc6672b81e8a4e096b39f82193f Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Thu, 11 Jun 2026 10:28:53 +0200 Subject: [PATCH 06/10] ref(plugin): Use Object token in binder instrumentation SentryBinderAdapter.onCallStart now returns a nullable Object token instead of an int cookie, and onCallEnd accepts that token. Update the emitted bytecode descriptors accordingly and pin the full method descriptors in the visitor tests. Co-Authored-By: Claude Fable 5 --- .../binder/BinderMethodVisitor.kt | 26 ++++++++++++++----- .../binder/BinderMethodVisitorTest.kt | 10 +++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt index 50de14c4d..64e38b61f 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt @@ -82,11 +82,11 @@ class BinderMethodVisitor( Opcodes.INVOKESTATIC, SENTRY_BINDER_ADAPTER, "onCallStart", - "(Ljava/lang/String;Ljava/lang/String;)I", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", false, ) - val cookieLocal = newLocal(Type.INT_TYPE) - storeLocal(cookieLocal) + val tokenLocal = newLocal(Type.getObjectType("java/lang/Object")) + storeLocal(tokenLocal) val tryStart = Label() val tryEnd = Label() @@ -105,14 +105,26 @@ class BinderMethodVisitor( super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) mv.visitLabel(tryEnd) - loadLocal(cookieLocal) - mv.visitMethodInsn(Opcodes.INVOKESTATIC, SENTRY_BINDER_ADAPTER, "onCallEnd", "(I)V", false) + loadLocal(tokenLocal) + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + SENTRY_BINDER_ADAPTER, + "onCallEnd", + "(Ljava/lang/Object;)V", + false, + ) mv.visitJumpInsn(Opcodes.GOTO, afterFinally) // catch-all handler: call onCallEnd then re-throw mv.visitLabel(catchHandler) - loadLocal(cookieLocal) - mv.visitMethodInsn(Opcodes.INVOKESTATIC, SENTRY_BINDER_ADAPTER, "onCallEnd", "(I)V", false) + loadLocal(tokenLocal) + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + SENTRY_BINDER_ADAPTER, + "onCallEnd", + "(Ljava/lang/Object;)V", + false, + ) mv.visitInsn(Opcodes.ATHROW) mv.visitLabel(afterFinally) diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitorTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitorTest.kt index dc6b403e4..7553d673a 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitorTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitorTest.kt @@ -66,11 +66,17 @@ class BinderMethodVisitorTest { val text = disassemble(instrumented) assertTrue( - text.contains("io/sentry/android/core/internal/binder/SentryBinderAdapter.onCallStart"), + text.contains( + "io/sentry/android/core/internal/binder/SentryBinderAdapter.onCallStart " + + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;" + ), "onCallStart should be emitted:\n$text", ) assertTrue( - text.contains("io/sentry/android/core/internal/binder/SentryBinderAdapter.onCallEnd"), + text.contains( + "io/sentry/android/core/internal/binder/SentryBinderAdapter.onCallEnd " + + "(Ljava/lang/Object;)V" + ), "onCallEnd should be emitted:\n$text", ) assertTrue( From 64e8f33af7a39b61f18ec5a3381cd6863ccfb5f3 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Thu, 11 Jun 2026 11:14:03 +0200 Subject: [PATCH 07/10] Fix naming, add docs --- .../io/sentry/android/gradle/extensions/BinderExtension.kt | 7 ++++++- .../gradle/extensions/TracingInstrumentationExtension.kt | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt index 30e1d1db0..262ecc8bd 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt @@ -5,6 +5,11 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property open class BinderExtension @Inject constructor(objects: ObjectFactory) { - /** Enables or disables Binder IPC call instrumentation. Defaults to true. */ + /** + * Enables or disables Binder IPC call instrumentation. Defaults to true. This requires + * sentry-android-core version 8.x.x or above, and needs to be enabled. See + * https://docs.sentry.io/platforms/android/configuration/gradle/#tracing-auto-instrumentation for + * more details. + */ val enabled: Property = objects.property(Boolean::class.java).convention(true) } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt index eb85ca6bc..b8e2b32db 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/TracingInstrumentationExtension.kt @@ -74,7 +74,7 @@ open class TracingInstrumentationExtension @Inject constructor(objects: ObjectFa val binder: BinderExtension = objects.newInstance(BinderExtension::class.java) - fun binderIpc(binderAction: Action) { + fun binder(binderAction: Action) { binderAction.execute(binder) } } From 7ebb52addad3b7009c5d7fe5c012ded013b392d7 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Thu, 11 Jun 2026 14:49:09 +0200 Subject: [PATCH 08/10] Rename leftovers to avoid any confusion, let's remove IPC and stick with "Binder" everywhere --- .../android/gradle/extensions/BinderExtension.kt | 2 +- .../SpanAddingClassVisitorFactory.kt | 4 ++-- .../binder/{BinderIpc.kt => Binder.kt} | 7 +++---- .../gradle/services/SentryModulesService.kt | 14 ++++++-------- .../io/sentry/android/gradle/util/Versions.kt | 2 +- .../gradle/util/SentryModulesCollectorTest.kt | 4 ++-- 6 files changed, 15 insertions(+), 18 deletions(-) rename plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/{BinderIpc.kt => Binder.kt} (77%) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt index 262ecc8bd..6d1c8fbcb 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/BinderExtension.kt @@ -7,7 +7,7 @@ import org.gradle.api.provider.Property open class BinderExtension @Inject constructor(objects: ObjectFactory) { /** * Enables or disables Binder IPC call instrumentation. Defaults to true. This requires - * sentry-android-core version 8.x.x or above, and needs to be enabled. See + * sentry-android-core version TODO or above, and needs to be enabled. See * https://docs.sentry.io/platforms/android/configuration/gradle/#tracing-auto-instrumentation for * more details. */ diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt index dd9df8eab..9ab4e2845 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt @@ -13,7 +13,7 @@ import io.sentry.android.gradle.instrumentation.androidx.sqlite.database.Android import io.sentry.android.gradle.instrumentation.androidx.sqlite.statement.AndroidXSQLiteStatement import io.sentry.android.gradle.instrumentation.appstart.Application import io.sentry.android.gradle.instrumentation.appstart.ContentProvider -import io.sentry.android.gradle.instrumentation.binder.BinderIpc +import io.sentry.android.gradle.instrumentation.binder.Binder import io.sentry.android.gradle.instrumentation.logcat.Logcat import io.sentry.android.gradle.instrumentation.logcat.LogcatLevel import io.sentry.android.gradle.instrumentation.okhttp.OkHttp @@ -109,7 +109,7 @@ abstract class SpanAddingClassVisitorFactory : RemappingInstrumentable().takeIf { sentryModulesService.isFileIOInstrEnabled() }, ComposeNavigation().takeIf { sentryModulesService.isComposeInstrEnabled() }, Logcat().takeIf { sentryModulesService.isLogcatInstrEnabled() }, - BinderIpc().takeIf { sentryModulesService.isBinderIpcInstrEnabled() }, + Binder().takeIf { sentryModulesService.isBinderInstrEnabled() }, Application().takeIf { sentryModulesService.isAppStartInstrEnabled() }, ContentProvider().takeIf { sentryModulesService.isAppStartInstrEnabled() }, ) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/Binder.kt similarity index 77% rename from plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt rename to plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/Binder.kt index d53ad602d..655c16800 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderIpc.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/Binder.kt @@ -4,13 +4,12 @@ import com.android.build.api.instrumentation.ClassContext import io.sentry.android.gradle.instrumentation.ClassInstrumentable import io.sentry.android.gradle.instrumentation.CommonClassVisitor import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory -import io.sentry.android.gradle.instrumentation.util.isSentryClass import org.objectweb.asm.ClassVisitor -class BinderIpc : ClassInstrumentable { +class Binder : ClassInstrumentable { companion object { - private const val CLASSNAME = "BinderIpc" + private const val CLASSNAME = "Binder" } override fun getVisitor( @@ -28,5 +27,5 @@ class BinderIpc : ClassInstrumentable { ) } - override fun isInstrumentable(data: ClassContext) = !data.isSentryClass() + override fun isInstrumentable(data: ClassContext) = true // !data.isSentryClass() } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/services/SentryModulesService.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/services/SentryModulesService.kt index 2ce5d360d..85744674f 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/services/SentryModulesService.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/services/SentryModulesService.kt @@ -44,8 +44,8 @@ abstract class SentryModulesService : features.add("AppStartInstrumentation") } - if (isBinderIpcInstrEnabled()) { - features.add("BinderIpcInstrumentation") + if (isBinderInstrEnabled()) { + features.add("BinderInstrumentation") } if (parameters.sourceContextEnabled.getOrElse(false)) { @@ -120,9 +120,7 @@ abstract class SentryModulesService : sentryModules.isAtLeast(SentryModules.SENTRY_ANDROID_CORE, SentryVersions.VERSION_APP_START) && parameters.appStartEnabled.get() - fun isBinderIpcInstrEnabled(): Boolean = - sentryModules.isAtLeast(SentryModules.SENTRY_ANDROID_CORE, SentryVersions.VERSION_BINDER_IPC) && - parameters.binderIpcEnabled.get() + fun isBinderInstrEnabled(): Boolean = parameters.binderEnabled.get() private fun Map.isAtLeast( module: ModuleIdentifier, @@ -137,7 +135,7 @@ abstract class SentryModulesService : sourceContextEnabled: Provider, dexguardEnabled: Provider, appStartEnabled: Provider, - binderIpcEnabled: Provider, + binderEnabled: Provider, ): Provider { return project.gradle.sharedServices.registerIfAbsent( getBuildServiceName(SentryModulesService::class.java), @@ -148,7 +146,7 @@ abstract class SentryModulesService : it.parameters.sourceContextEnabled.setDisallowChanges(sourceContextEnabled) it.parameters.dexguardEnabled.setDisallowChanges(dexguardEnabled) it.parameters.appStartEnabled.setDisallowChanges(appStartEnabled) - it.parameters.binderIpcEnabled.setDisallowChanges(binderIpcEnabled) + it.parameters.binderEnabled.setDisallowChanges(binderEnabled) } } } @@ -167,6 +165,6 @@ abstract class SentryModulesService : @get:Input val appStartEnabled: Property - @get:Input val binderIpcEnabled: Property + @get:Input val binderEnabled: Property } } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt index 951daf073..57cc9c02d 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt @@ -35,7 +35,7 @@ internal object SentryVersions { internal val VERSION_SQLITE = SemVer(6, 21, 0) internal val VERSION_ANDROID_OKHTTP_LISTENER = SemVer(6, 20, 0) internal val VERSION_OKHTTP = SemVer(7, 0, 0) - internal val VERSION_BINDER_IPC = SemVer(8, 40, 0) + internal val VERSION_BINDER = SemVer(8, 40, 0) } internal object SentryModules { diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/util/SentryModulesCollectorTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/util/SentryModulesCollectorTest.kt index 74ee2ceff..fe99af692 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/util/SentryModulesCollectorTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/util/SentryModulesCollectorTest.kt @@ -57,7 +57,7 @@ class SentryModulesCollectorTest { val sourceContextEnabled = fakeProject.provider { true } val dexguardEnabled = fakeProject.provider { true } val appStartEnabled = fakeProject.provider { true } - val binderIpcEnabled = fakeProject.provider { true } + val binderEnabled = fakeProject.provider { true } val project = spy(fakeProject) whenever(project.logger).thenReturn(logger) @@ -70,7 +70,7 @@ class SentryModulesCollectorTest { sourceContextEnabled, dexguardEnabled, appStartEnabled, - binderIpcEnabled, + binderEnabled, ) return project From ce9013dba0eb33274f98ba3cc4964290ba32ee9c Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 15 Jun 2026 09:34:38 +0200 Subject: [PATCH 09/10] ref(plugin): Simplify binder method visitor and rename binderEnabled flag Rewrite the static/instance opcode guard in BinderMethodVisitor as an explicit check and document why it is required, extract the duplicated onCallEnd emission, and add comments explaining the try/finally rewrite. Rename binderIpcEnabled to binderEnabled and enable the isSentryClass guard in Binder. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../android/gradle/AndroidComponentsConfig.kt | 4 +- .../SpanAddingClassVisitorFactory.kt | 2 +- .../gradle/instrumentation/binder/Binder.kt | 3 +- .../binder/BinderMethodVisitor.kt | 81 ++++++++++++------- .../fakes/TestSpanAddingParameters.kt | 2 +- 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt index ffd9de1ac..864a4bbdc 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt @@ -229,9 +229,7 @@ fun ApplicationAndroidComponentsExtension.configure( params.appStartEnabled.setDisallowChanges( extension.tracingInstrumentation.appStart.enabled ) - params.binderIpcEnabled.setDisallowChanges( - extension.tracingInstrumentation.binder.enabled - ) + params.binderEnabled.setDisallowChanges(extension.tracingInstrumentation.binder.enabled) params.tmpDir.set( project.layout.buildDirectory.dir("sentry-logs/instrumentation/${variant.name}") ) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt index 9ab4e2845..66475fd77 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt @@ -65,7 +65,7 @@ abstract class SpanAddingClassVisitorFactory : @get:Input val appStartEnabled: Property - @get:Input val binderIpcEnabled: Property + @get:Input val binderEnabled: Property } private val instrumentable: ClassInstrumentable diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/Binder.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/Binder.kt index 655c16800..af95257ca 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/Binder.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/Binder.kt @@ -4,6 +4,7 @@ import com.android.build.api.instrumentation.ClassContext import io.sentry.android.gradle.instrumentation.ClassInstrumentable import io.sentry.android.gradle.instrumentation.CommonClassVisitor import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory +import io.sentry.android.gradle.instrumentation.util.isSentryClass import org.objectweb.asm.ClassVisitor class Binder : ClassInstrumentable { @@ -27,5 +28,5 @@ class Binder : ClassInstrumentable { ) } - override fun isInstrumentable(data: ClassContext) = true // !data.isSentryClass() + override fun isInstrumentable(data: ClassContext) = !data.isSentryClass() } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt index 64e38b61f..e4150d999 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/binder/BinderMethodVisitor.kt @@ -46,21 +46,50 @@ class BinderMethodVisitor( isInterface: Boolean, ) { val spec = BinderMethodRegistry.lookup(owner, name) + + // Not a tracked binder call - emit the original instruction untouched. if (spec == null) { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) return } - val isInstanceCall = opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKEINTERFACE - val isStaticCall = opcode == Opcodes.INVOKESTATIC - if (spec.isStatic && !isStaticCall || !spec.isStatic && !isInstanceCall) { + // The registry is keyed by owner + name only, so a lookup can match a call whose invocation + // kind (static vs. instance) differs from the spec - e.g. an unrelated static method that + // happens to share a name with a tracked instance method (many tracked names are generic, + // like commit/cancel/acquire/release). That distinction decides whether a receiver sits on + // the stack below the arguments, so instrumenting a mismatched call would spill the wrong + // number of values and emit invalid bytecode. Skip it and emit the original instruction. + val opcodeMatchesSpec = + if (spec.isStatic) { + opcode == Opcodes.INVOKESTATIC + } else { + opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKEINTERFACE + } + if (!opcodeMatchesSpec) { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) return } + // We rewrite `target(args...)` into the equivalent of: + // + // Object token = SentryBinderAdapter.onCallStart(component, name); + // try { + // target(args...); + // SentryBinderAdapter.onCallEnd(token); + // } catch (Throwable t) { + // SentryBinderAdapter.onCallEnd(token); + // throw t; + // } + // + // Adapter calls are emitted via `mv` (the delegate) rather than the GeneratorAdapter helpers + // (push/invokeStatic) on purpose: those helpers route through visitMethodInsn - this very + // override - and would recurse. `super.visitMethodInsn` is reserved for the original target + // call so it still passes through any downstream visitors. + + // The arguments (and the receiver, for instance calls) are already on the stack. Spill them + // into locals so we can run onCallStart first and reload them inside the try block. The stack + // is LIFO, so arguments are popped in reverse order. val argTypes = Method(name, descriptor).argumentTypes - - // Save arguments from the stack into temp locals (reverse order for LIFO) val argLocals = IntArray(argTypes.size) for (i in argLocals.size - 1 downTo 0) { argLocals[i] = newLocal(argTypes[i]) @@ -69,13 +98,12 @@ class BinderMethodVisitor( val receiverLocal = if (!spec.isStatic) { - val local = newLocal(Type.getObjectType(owner)) - storeLocal(local) - local + newLocal(Type.getObjectType(owner)).also { storeLocal(it) } } else { -1 } + // Object token = SentryBinderAdapter.onCallStart(component, name); mv.visitLdcInsn(spec.component) mv.visitLdcInsn(name) mv.visitMethodInsn( @@ -88,14 +116,26 @@ class BinderMethodVisitor( val tokenLocal = newLocal(Type.getObjectType("java/lang/Object")) storeLocal(tokenLocal) + // SentryBinderAdapter.onCallEnd(token); - emitted on both the normal and the exceptional path. + fun emitOnCallEnd() { + loadLocal(tokenLocal) + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + SENTRY_BINDER_ADAPTER, + "onCallEnd", + "(Ljava/lang/Object;)V", + false, + ) + } + val tryStart = Label() val tryEnd = Label() val catchHandler = Label() val afterFinally = Label() - mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, null) - mv.visitLabel(tryStart) + // try { target(args...); onCallEnd(token); } + mv.visitLabel(tryStart) if (!spec.isStatic) { loadLocal(receiverLocal) } @@ -103,28 +143,13 @@ class BinderMethodVisitor( loadLocal(local) } super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) - mv.visitLabel(tryEnd) - loadLocal(tokenLocal) - mv.visitMethodInsn( - Opcodes.INVOKESTATIC, - SENTRY_BINDER_ADAPTER, - "onCallEnd", - "(Ljava/lang/Object;)V", - false, - ) + emitOnCallEnd() mv.visitJumpInsn(Opcodes.GOTO, afterFinally) - // catch-all handler: call onCallEnd then re-throw + // catch (Throwable t) { onCallEnd(token); throw t; } mv.visitLabel(catchHandler) - loadLocal(tokenLocal) - mv.visitMethodInsn( - Opcodes.INVOKESTATIC, - SENTRY_BINDER_ADAPTER, - "onCallEnd", - "(Ljava/lang/Object;)V", - false, - ) + emitOnCallEnd() mv.visitInsn(Opcodes.ATHROW) mv.visitLabel(afterFinally) diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/fakes/TestSpanAddingParameters.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/fakes/TestSpanAddingParameters.kt index 1f1eaf0e1..d21f87517 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/fakes/TestSpanAddingParameters.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/fakes/TestSpanAddingParameters.kt @@ -48,6 +48,6 @@ class TestSpanAddingParameters( override val appStartEnabled: Property get() = TODO() - override val binderIpcEnabled: Property + override val binderEnabled: Property get() = TODO() } From c99a18db3d35d849e8a8bbd8fbb7b24ef342e5b5 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 15 Jun 2026 09:35:21 +0200 Subject: [PATCH 10/10] Add Todo --- .../src/main/kotlin/io/sentry/android/gradle/util/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt index 57cc9c02d..9015c5073 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Versions.kt @@ -35,7 +35,7 @@ internal object SentryVersions { internal val VERSION_SQLITE = SemVer(6, 21, 0) internal val VERSION_ANDROID_OKHTTP_LISTENER = SemVer(6, 20, 0) internal val VERSION_OKHTTP = SemVer(7, 0, 0) - internal val VERSION_BINDER = SemVer(8, 40, 0) + internal val VERSION_BINDER = SemVer(8, 40, 0) // TODO set right version } internal object SentryModules {