diff --git a/embrace-android-core/lint-baseline.xml b/embrace-android-core/lint-baseline.xml index 261092375e..0f7a427796 100644 --- a/embrace-android-core/lint-baseline.xml +++ b/embrace-android-core/lint-baseline.xml @@ -12,17 +12,6 @@ column="12"/> - - - - (Ljava/lang/String;Ljava/lang/Throwable;)V - public final fun getCustomPath ()Ljava/lang/String; -} - -public final class io/embrace/android/embracesdk/okhttp3/EmbraceOkHttp3ApplicationInterceptor : okhttp3/Interceptor { - public fun (Lio/embrace/android/embracesdk/Embrace;Lio/embrace/android/embracesdk/internal/EmbraceInternalApi;)V - public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; -} - -public final class io/embrace/android/embracesdk/okhttp3/EmbraceOkHttp3NetworkInterceptor : okhttp3/Interceptor { - public fun (Lio/embrace/android/embracesdk/Embrace;Lio/embrace/android/embracesdk/internal/EmbraceInternalApi;)V - public fun (Lio/embrace/android/embracesdk/Embrace;Lio/embrace/android/embracesdk/internal/EmbraceInternalApi;Lio/embrace/android/embracesdk/internal/clock/Clock;)V - public synthetic fun (Lio/embrace/android/embracesdk/Embrace;Lio/embrace/android/embracesdk/internal/EmbraceInternalApi;Lio/embrace/android/embracesdk/internal/clock/Clock;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; -} - -public final class io/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient { -} - -public final class io/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient$Builder { - public static fun _constructorOnPostBody (Lokhttp3/OkHttpClient$Builder;)V - public static fun _preBuild (Lokhttp3/OkHttpClient$Builder;)V -} - diff --git a/embrace-android-okhttp3/build.gradle.kts b/embrace-android-okhttp3/build.gradle.kts index 479c4529d6..de0d20e909 100644 --- a/embrace-android-okhttp3/build.gradle.kts +++ b/embrace-android-okhttp3/build.gradle.kts @@ -20,4 +20,5 @@ dependencies { testImplementation(project(":embrace-android-infra")) testImplementation(project(":embrace-android-sdk")) testImplementation(project(":embrace-internal-api")) + implementation(libs.lifecycle.runtime) } diff --git a/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OkHttpBytecodeEntrypoint.kt b/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OkHttpBytecodeEntrypoint.kt new file mode 100644 index 0000000000..9d6951e762 --- /dev/null +++ b/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OkHttpBytecodeEntrypoint.kt @@ -0,0 +1,65 @@ +package io.embrace.android.embracesdk.internal.instrumentation.bytecode + +import androidx.annotation.Keep +import io.embrace.android.embracesdk.Embrace +import io.embrace.android.embracesdk.internal.EmbraceInternalApi +import io.embrace.android.embracesdk.okhttp3.EmbraceOkHttp3ApplicationInterceptor +import io.embrace.android.embracesdk.okhttp3.EmbraceOkHttp3NetworkInterceptor +import okhttp3.Interceptor +import okhttp3.OkHttpClient + +/** + * @hide + */ +@Keep +object OkHttpBytecodeEntrypoint { + + /** + * As there was a way to clear the injected interceptors during the OkHttpClient + * initialization using the builder, we are hooking the build method as well, instead of + * just the Builder constructor. + * + * Once the build method is called, OkHTTP mushes everything and returns the OkHttpClient + * instance, where the developer has no way to alter any of the interceptors during or + * after this point, without having to rebuild the client. + */ + @Keep + @JvmStatic + fun build(thiz: OkHttpClient.Builder) { + addEmbraceInterceptors(thiz) + } + + /** + * Adds embrace interceptors if they don't exist already to the OkHTTPClient provided. + * + * @param thiz the OkHttpClient builder in matter. + */ + private fun addEmbraceInterceptors(thiz: OkHttpClient.Builder) { + val internalApi = EmbraceInternalApi.getInstance() + try { + val embrace = Embrace.getInstance() + addInterceptor( + thiz.interceptors(), + EmbraceOkHttp3ApplicationInterceptor(embrace, internalApi) + ) + addInterceptor( + thiz.networkInterceptors(), + EmbraceOkHttp3NetworkInterceptor(embrace, internalApi) + ) + } catch (error: Throwable) { + internalApi.internalInterface.logInternalError(error) + } + } + + /** + * Adds the interceptor to the interceptors list if it doesn't exist already. + */ + private fun addInterceptor( + interceptors: MutableList?, + interceptor: Interceptor, + ) { + if (interceptors != null && !interceptors.any { interceptor.javaClass.isInstance(it) }) { + interceptors.add(0, interceptor) + } + } +} diff --git a/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient.java b/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient.java deleted file mode 100644 index eb76efdb10..0000000000 --- a/embrace-android-okhttp3/src/main/java/io/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.embrace.android.embracesdk.okhttp3.swazzle.callback.okhttp3; - -import java.util.List; - -import io.embrace.android.embracesdk.Embrace; -import io.embrace.android.embracesdk.internal.EmbraceInternalApi; -import io.embrace.android.embracesdk.okhttp3.EmbraceOkHttp3ApplicationInterceptor; -import io.embrace.android.embracesdk.okhttp3.EmbraceOkHttp3NetworkInterceptor; -import okhttp3.Interceptor; - -/** - * Callback hooks for the okhttp3.OkHttpClient class. - */ -public final class OkHttpClient { - - private OkHttpClient() { - } - - public static final class Builder { - - private Builder() { - } - - /** - * As there was a way to clear the injected interceptors during the OkHttpClient - * initialization using the builder, we are hooking the build method as well, instead of - * just the Builder constructor. - *

- * Once the build method is called, OkHTTP mushes everything and returns the OkHttpClient - * instance, where the developer has no way to alter any of the interceptors during or - * after this point, without having to rebuild the client. - */ - @SuppressWarnings("MethodNameCheck") - public static void _preBuild(okhttp3.OkHttpClient.Builder thiz) { - addEmbraceInterceptors(thiz); - } - - @SuppressWarnings("MethodNameCheck") - public static void _constructorOnPostBody(okhttp3.OkHttpClient.Builder thiz) { - addEmbraceInterceptors(thiz); - } - - /** - * Adds embrace interceptors if they don't exist already to the OkHTTPClient provided. - * - * @param thiz the OkHttpClient builder in matter. - */ - private static void addEmbraceInterceptors(okhttp3.OkHttpClient.Builder thiz) { - try { - addInterceptor(thiz.interceptors(), new EmbraceOkHttp3ApplicationInterceptor(Embrace.getInstance(), EmbraceInternalApi.getInstance())); - addInterceptor(thiz.networkInterceptors(), new EmbraceOkHttp3NetworkInterceptor(Embrace.getInstance(), EmbraceInternalApi.getInstance())); - } catch (NoSuchMethodError exception) { - // The customer may be overwriting OkHttpClient with their own implementation, and some of the - // methods we use are missing. - logInternalError(exception); - } catch (Exception exception) { - logInternalError(exception); - } - } - - /** - * Adds the interceptor to the interceptors list if it doesn't exist already. - * - * @param interceptors list of existing interceptors. - * @param interceptor interceptor to be added. - */ - private static void addInterceptor(List interceptors, - Interceptor interceptor) { - if (interceptors != null && !containsInstance(interceptors, interceptor.getClass())) { - interceptors.add(0, interceptor); - } - } - - /** - * Checks for the existence in the elements list of an instance of the same class as the - * one provided in the arguments. - * - * @param elementsList list of elements. - * @param clazz class of the instance that's being checked if exists. - * @return if an instance of the provided class exists in the list of elements. - */ - private static boolean containsInstance(List elementsList, - Class clazz) { - for (T classInstance : elementsList) { - if (clazz.isInstance(classInstance)) { - return true; - } - } - return false; - } - - private static void logInternalError(Throwable throwable) { - EmbraceInternalApi.getInstance().getInternalInterface().logInternalError(throwable); - } - } -} diff --git a/embrace-android-sdk/api/embrace-android-sdk.api b/embrace-android-sdk/api/embrace-android-sdk.api index 8e06c17e74..96a2f21399 100644 --- a/embrace-android-sdk/api/embrace-android-sdk.api +++ b/embrace-android-sdk/api/embrace-android-sdk.api @@ -82,18 +82,18 @@ public final class io/embrace/android/embracesdk/Embrace$Companion { public final fun getInstance ()Lio/embrace/android/embracesdk/Embrace; } -public final class io/embrace/android/embracesdk/ViewSwazzledHooks { +public final class io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint { + public static final field INSTANCE Lio/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint; + public static final fun onClick (Landroid/view/View;)V } -public final class io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener { - public static fun _preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V +public final class io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint { + public static final field INSTANCE Lio/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint; + public static final fun onLongClick (Landroid/view/View;)V } -public final class io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener { - public static fun _preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V -} - -public final class io/embrace/android/embracesdk/WebViewClientSwazzledHooks { - public static fun _preOnPageStarted (Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V +public final class io/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint { + public static final field INSTANCE Lio/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint; + public static final fun onPageStarted (Ljava/lang/String;)V } diff --git a/embrace-android-sdk/embrace-proguard.cfg b/embrace-android-sdk/embrace-proguard.cfg index 024a96b22e..625cbded8e 100644 --- a/embrace-android-sdk/embrace-proguard.cfg +++ b/embrace-android-sdk/embrace-proguard.cfg @@ -12,7 +12,6 @@ ## Keep classes used by hosted SDKs -keep class io.embrace.android.embracesdk.Embrace$AppFramework { *; } -keep class io.embrace.android.embracesdk.Embrace$LastRunEndState { *; } --keep class io.embrace.android.embracesdk.Severity { *; } -keep public class * implements io.embrace.android.embracesdk.internal.EmbraceInternalInterface { *; } ## Keep classes used from native code @@ -25,6 +24,12 @@ -keep class io.embrace.android.embracesdk.internal.anr.ndk.NativeThreadSamplerNdkDelegate { *; } -keep class io.embrace.android.embracesdk.internal.ndk.jni.JniDelegateImpl { *; } +## Keep gradle plugin hooks +-keep class io.embrace.android.embracesdk.internal.config.instrumented.** { *; } + +## Keep internal files for tracing +-keep class io.embrace.android.embracesdk.internal.injection.** { *; } + ## OpenTelemetry Java SDK -keep class io.opentelemetry.api.trace.StatusCode { *; } -dontwarn com.google.auto.value.extension.memoized.Memoized @@ -46,14 +51,3 @@ -dontwarn com.google.errorprone.annotations.MustBeClosed -dontwarn com.google.firebase.messaging.RemoteMessage$Notification -dontwarn com.google.firebase.messaging.RemoteMessage - -## Keep gradle plugin hooks --keep class io.embrace.android.embracesdk.okhttp3.** { *; } --keep class io.embrace.android.embracesdk.ViewSwazzledHooks { *; } --keep class io.embrace.android.embracesdk.WebViewClientSwazzledHooks { *; } --keep class io.embrace.android.embracesdk.WebViewChromeClientSwazzledHooks { *; } --keep class io.embrace.android.embracesdk.fcm.swazzle.callback.com.android.fcm.FirebaseSwazzledHooks { *; } --keep class io.embrace.android.embracesdk.internal.config.instrumented.** { *; } - -## Keep internal files for tracing --keep class io.embrace.android.embracesdk.internal.injection.** { *; } diff --git a/embrace-android-sdk/lint-baseline.xml b/embrace-android-sdk/lint-baseline.xml index 143e25e8df..8365a556b8 100644 --- a/embrace-android-sdk/lint-baseline.xml +++ b/embrace-android-sdk/lint-baseline.xml @@ -1,5 +1,5 @@ - + + errorLine1="public object WebViewClientBytecodeEntrypoint {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/java/io/embrace/android/embracesdk/WebViewClientBytecodeEntrypoint.kt" + line="13" + column="15"/> point = null; - try { - point = new Pair<>(view.getX(), view.getY()); - } catch (Exception e) { - point = new Pair<>(0.0F, 0.0F); - } - EmbraceInternalApi.getInstance().getInternalInterface().logTap(point, viewName, breadcrumbType); - } catch (NoSuchMethodError error) { - // The customer may be overwriting View with their own implementation, and some of the - // methods we use are missing. - logError(error); - } catch (Exception exception) { - logError(exception); - } - } - - private static void logError(@NonNull Throwable throwable) { - EmbraceInternalApi.getInstance().getInternalInterface().logInternalError(throwable); - } - - @InternalApi - public static final class OnClickListener { - private OnClickListener() { - } - - @SuppressWarnings("MethodNameCheck") - public static void _preOnClick(android.view.View.OnClickListener thiz, android.view.View view) { - logOnClickEvent(view, TapBreadcrumbType.TAP); - } - } - - @InternalApi - public static final class OnLongClickListener { - private OnLongClickListener() { - } - - @SuppressWarnings("MethodNameCheck") - public static void _preOnLongClick(android.view.View.OnLongClickListener thiz, android.view.View view) { - if (thiz != null) { - logOnClickEvent(view, TapBreadcrumbType.LONG_PRESS); - } - } - } -} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/WebViewClientSwazzledHooks.java b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/WebViewClientSwazzledHooks.java deleted file mode 100644 index a7b03c00a5..0000000000 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/WebViewClientSwazzledHooks.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.embrace.android.embracesdk; - -import androidx.annotation.Nullable; - -import io.embrace.android.embracesdk.annotation.InternalApi; - -/** - * @hide - */ -@InternalApi -public final class WebViewClientSwazzledHooks { - - private WebViewClientSwazzledHooks() { - } - - @SuppressWarnings("MethodNameCheck") - public static void _preOnPageStarted(@Nullable android.webkit.WebView view, - @Nullable java.lang.String url, - @Nullable android.graphics.Bitmap favicon) { - Embrace.getInstance().logWebView(url); - } -} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.kt new file mode 100644 index 0000000000..ddd164e6bc --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.kt @@ -0,0 +1,20 @@ +package io.embrace.android.embracesdk.internal.instrumentation.bytecode + +import android.view.View +import androidx.annotation.Keep +import io.embrace.android.embracesdk.annotation.InternalApi +import io.embrace.android.embracesdk.internal.payload.TapBreadcrumb.TapBreadcrumbType + +/** + * @hide + */ +@InternalApi +@Keep +public object OnClickBytecodeEntrypoint { + + @Keep + @JvmStatic + public fun onClick(view: View) { + logTouchEvent(view, TapBreadcrumbType.TAP) + } +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.kt new file mode 100644 index 0000000000..e21db4736c --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.kt @@ -0,0 +1,20 @@ +package io.embrace.android.embracesdk.internal.instrumentation.bytecode + +import android.view.View +import androidx.annotation.Keep +import io.embrace.android.embracesdk.annotation.InternalApi +import io.embrace.android.embracesdk.internal.payload.TapBreadcrumb.TapBreadcrumbType + +/** + * @hide + */ +@InternalApi +@Keep +public object OnLongClickBytecodeEntrypoint { + + @Keep + @JvmStatic + public fun onLongClick(view: View) { + logTouchEvent(view, TapBreadcrumbType.LONG_PRESS) + } +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/TouchEvent.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/TouchEvent.kt new file mode 100644 index 0000000000..4fcf522fd2 --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/TouchEvent.kt @@ -0,0 +1,29 @@ +package io.embrace.android.embracesdk.internal.instrumentation.bytecode + +import android.view.View +import io.embrace.android.embracesdk.internal.EmbraceInternalApi +import io.embrace.android.embracesdk.internal.payload.TapBreadcrumb.TapBreadcrumbType + +private const val UNKNOWN_ELEMENT_NAME = "Unknown element" + +internal fun logTouchEvent(view: View, breadcrumbType: TapBreadcrumbType) { + try { + val viewName = try { + view.resources.getResourceName(view.id) + } catch (e: Exception) { + UNKNOWN_ELEMENT_NAME + } + val point: Pair = try { + Pair(view.x, view.y) + } catch (e: Exception) { + Pair(0.0f, 0.0f) + } + EmbraceInternalApi.getInstance().internalInterface.logTap( + point, + viewName, + breadcrumbType + ) + } catch (throwable: Throwable) { + EmbraceInternalApi.getInstance().internalInterface.logInternalError(throwable) + } +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint.kt new file mode 100644 index 0000000000..67b68feff6 --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint.kt @@ -0,0 +1,19 @@ +package io.embrace.android.embracesdk.internal.instrumentation.bytecode + +import androidx.annotation.Keep +import io.embrace.android.embracesdk.Embrace +import io.embrace.android.embracesdk.annotation.InternalApi + +/** + * @hide + */ +@InternalApi +@Keep +public object WebViewClientBytecodeEntrypoint { + + @JvmStatic + @Keep + public fun onPageStarted(url: String?,) { + Embrace.getInstance().logWebView(url) + } +} diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/BytecodeTestParams.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/BytecodeTestParams.kt index dadf7323af..83a74ce57f 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/BytecodeTestParams.kt +++ b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/BytecodeTestParams.kt @@ -17,32 +17,32 @@ internal const val ASM_CLASS_READER_FLAGS = ClassReader.EXPAND_FRAMES // com.android.build.gradle.internal.instrumentation.AsmInstrumentationManager#getClassWriterFlags internal const val ASM_CLASS_WRITER_FLAGS = ClassWriter.COMPUTE_FRAMES -internal typealias ClassVisitorFactory = (nextVisitor: ClassVisitor) -> ClassVisitor +internal typealias ClassVisitorFactory = (nextVisitor: ClassVisitor, params: BytecodeTestParams) -> ClassVisitor /** * Test parameters used to instrument bytecode. */ class BytecodeTestParams( - clz: Class<*>, - val qualifiedClzName: String = clz.name, - val simpleClzName: String = clz.simpleName, + clz: KClass<*>, + val qualifiedClzName: String = clz.java.name, + val simpleClzName: String = clz.java.simpleName, val expectedOutput: String = "${simpleClzName}_expected.txt", - val factory: ClassVisitorFactory = { nextVisitor -> + val factory: ClassVisitorFactory = { nextVisitor, _ -> nextVisitor - } + }, ) { companion object { fun forInnerClass( - kClass: KClass<*>, + clz: KClass<*>, innerClzName: String, - factory: ClassVisitorFactory = { nextVisitor -> + factory: ClassVisitorFactory = { nextVisitor, _ -> nextVisitor - } + }, ): BytecodeTestParams { return BytecodeTestParams( - kClass.java, - qualifiedClzName = "${kClass.java.name}$innerClzName", + clz, + qualifiedClzName = "${clz.java.name}$innerClzName", factory = factory ) } diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/FakeBytecodeInstrumentationParams.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/FakeBytecodeInstrumentationParams.kt new file mode 100644 index 0000000000..2ee01efd47 --- /dev/null +++ b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/FakeBytecodeInstrumentationParams.kt @@ -0,0 +1,22 @@ +package io.embrace.gradle.plugin.instrumentation + +import io.embrace.android.gradle.plugin.instrumentation.BytecodeInstrumentationParams +import io.embrace.android.gradle.plugin.instrumentation.ClassInstrumentationFilter +import io.embrace.android.gradle.plugin.instrumentation.config.model.VariantConfig +import org.gradle.api.provider.Property + +class FakeBytecodeInstrumentationParams( + override val disabled: Property = fakeProperty(false), + override val shouldInstrumentFirebaseMessaging: Property = fakeProperty(false), + override val shouldInstrumentWebview: Property = fakeProperty(true), + override val shouldInstrumentOkHttp: Property = fakeProperty(true), + override val shouldInstrumentOnLongClick: Property = fakeProperty(true), + override val shouldInstrumentOnClick: Property = fakeProperty(true), +) : BytecodeInstrumentationParams { + override val config: Property + get() = TODO("Not yet implemented") + override val encodedSharedObjectFilesMap: Property + get() = TODO("Not yet implemented") + override val classInstrumentationFilter: Property + get() = TODO("Not yet implemented") +} diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/FakeProperty.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/FakeProperty.kt new file mode 100644 index 0000000000..1deedd59ae --- /dev/null +++ b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/FakeProperty.kt @@ -0,0 +1,12 @@ +package io.embrace.gradle.plugin.instrumentation + +import io.mockk.every +import io.mockk.mockk +import org.gradle.api.provider.Property + +@Suppress("UNCHECKED_CAST") +fun fakeProperty(value: T): Property { + return mockk(relaxed = true) { + every { get() } returns value as (T & Any) + } +} diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentationRunner.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentationRunner.kt index 1ff7328d9e..f84f2dce89 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentationRunner.kt +++ b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentationRunner.kt @@ -21,16 +21,14 @@ object InstrumentationRunner { * is failed if it differs. */ fun runInstrumentationAndCompareOutput(params: BytecodeTestParams) { - with(params) { - val output = runInstrumentation(qualifiedClzName, factory) - assertEquals( - "The bytecode representation has changed from the known valid " + - "output. Please confirm whether this is intentional by comparing the " + - "input/output generated via TraceClassVisitor.", - loadResourceAsText(expectedOutput), - sanitizeOutput(output) - ) - } + val output = runInstrumentation(params) + assertEquals( + "The bytecode representation has changed from the known valid " + + "output. Please confirm whether this is intentional by comparing the " + + "input/output generated via TraceClassVisitor.", + loadResourceAsText(params.expectedOutput), + sanitizeOutput(output) + ) } /** @@ -38,17 +36,16 @@ object InstrumentationRunner { * the bytecode output. */ private fun runInstrumentation( - fqClzName: String, - factory: ClassVisitorFactory + params: BytecodeTestParams ): String { - val reader = ClassReader(fqClzName) + val reader = ClassReader(params.qualifiedClzName) val writer = ClassWriter(reader, ASM_CLASS_WRITER_FLAGS) // visit the class with a TraceClassVisitor which prints a textual representation // of the generated bytecode. val stringWriter = StringWriter() val traceVisitor = TraceClassVisitor(writer, PrintWriter(stringWriter)) - reader.accept(factory(traceVisitor), ASM_CLASS_READER_FLAGS) + reader.accept(params.factory(traceVisitor, params), ASM_CLASS_READER_FLAGS) // perform a sanity check that the bytecode is well-formed val bytecode = writer.toByteArray() @@ -61,7 +58,7 @@ object InstrumentationRunner { /** * Loads a resource and reads it as a [String]. */ - fun loadResourceAsText(resName: String): String { + private fun loadResourceAsText(resName: String): String { val classLoader = checkNotNull(javaClass.classLoader) val res = classLoader.getResourceAsStream(resName) ?: error("Could not find expected fixture resource named '$resName'") diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentedBytecodeTest.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentedBytecodeTest.kt index e7f1c3fe05..3032a2f368 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentedBytecodeTest.kt +++ b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentedBytecodeTest.kt @@ -1,5 +1,31 @@ package io.embrace.gradle.plugin.instrumentation +import io.embrace.android.gradle.plugin.instrumentation.VisitorFactoryImpl +import io.embrace.android.gradle.plugin.instrumentation.json.readBytecodeInstrumentationFeatures +import io.embrace.test.fixtures.ActivityOnClickListener +import io.embrace.test.fixtures.AnonInnerClassOnClickListener +import io.embrace.test.fixtures.AnonInnerClassOnLongClickListener +import io.embrace.test.fixtures.ControlObject +import io.embrace.test.fixtures.CustomOnClickListener +import io.embrace.test.fixtures.CustomOnLongClickListener +import io.embrace.test.fixtures.CustomWebViewClient +import io.embrace.test.fixtures.ExtendedCustomWebViewClient +import io.embrace.test.fixtures.ExtendedOnClickListener +import io.embrace.test.fixtures.ExtendedOnLongClickListener +import io.embrace.test.fixtures.FragmentOnClickListener +import io.embrace.test.fixtures.JavaAnonOnClickListener +import io.embrace.test.fixtures.JavaLambdaOnClickListener +import io.embrace.test.fixtures.JavaLambdaOnLongClickListener +import io.embrace.test.fixtures.JavaNested +import io.embrace.test.fixtures.KotlinNested +import io.embrace.test.fixtures.KotlinNestedOnLongClick +import io.embrace.test.fixtures.KotlinObjectOnClickListener +import io.embrace.test.fixtures.MissingInterfaceOnClickListener +import io.embrace.test.fixtures.MissingInterfaceOnLongClickListener +import io.embrace.test.fixtures.MissingOverrideOnClickListener +import io.embrace.test.fixtures.MissingOverrideOnLongClickListener +import io.embrace.test.fixtures.NoOverrideWebViewClient +import io.embrace.test.fixtures.VirtualMethodRefNamedOnClick import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -7,12 +33,44 @@ import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassVisitor import org.objectweb.asm.util.TraceClassVisitor +private val features = readBytecodeInstrumentationFeatures() +private val factory = VisitorFactoryImpl(ASM_API_VERSION, FakeBytecodeInstrumentationParams()) + +private fun createFactory( + params: BytecodeTestParams, + visitor: ClassVisitor, + name: String, + superClassName: String? = null, +): ClassVisitor { + val superClasses = when (superClassName) { + null -> emptyList() + else -> listOf(superClassName) + } + return factory.createClassVisitor( + feature = features.single { it.name == name }, + classContext = FakeClassContext(FakeClassData(className = params.qualifiedClzName, superClasses = superClasses)), + nextVisitor = visitor + ) +} + +private val onClickFactory: ClassVisitorFactory = { visitor, params -> + createFactory(params, visitor, "on_click") +} + +private val onLongClickFactory: ClassVisitorFactory = { visitor, params -> + createFactory(params, visitor, "on_long_click") +} + +private val webviewFactory: ClassVisitorFactory = { visitor, params -> + createFactory(params, visitor, "webview_page_start", "android.webkit.WebViewClient") +} + /** * Verifies that a [ClassVisitor] produces the correct bytecode output for a given class. * - * For example, if a class implements [View.OnClickListener] then the embrace gradle plugin should - * instrument the bytecode so that the first line of the onClick method contains a call to - * [ViewSwazzledHooks._preOnClick]. If a class does not implement the interface, then its + * For example, if a class implements View.OnClickListener then the embrace gradle plugin should + * instrument the bytecode so that the first line of the onClick method contains a call to our API + * If a class does not implement the interface, then its * bytecode should remain the same. * * The test achieves this verification using the following approach: @@ -22,22 +80,54 @@ import org.objectweb.asm.util.TraceClassVisitor * 3. Process the class using the [ClassVisitor] instance that instruments the bytecode * 4. Compare the bytecode representation obtained from a [TraceClassVisitor] with a known output * - * To add more test cases please use [instrumentedBytecodeTestCases] and read the README. - * * For more information on WebObject ASM, see https://asm.ow2.io/ */ @RunWith(Parameterized::class) class InstrumentedBytecodeTest( - private val params: BytecodeTestParams + private val params: BytecodeTestParams, ) { - /** - * To add more test cases please use [instrumentedBytecodeTestCases] and read the README. - */ companion object { + @JvmStatic @Parameterized.Parameters(name = "{index}: {0}") - fun testCases() = instrumentedBytecodeTestCases() + fun testCases() = listOf( + BytecodeTestParams(clz = ActivityOnClickListener::class, factory = onClickFactory), + BytecodeTestParams(clz = ControlObject::class, factory = onClickFactory), + BytecodeTestParams(clz = CustomOnClickListener::class, factory = onClickFactory), + BytecodeTestParams(clz = ExtendedOnClickListener::class, factory = onClickFactory), + BytecodeTestParams(clz = FragmentOnClickListener::class, factory = onClickFactory), + BytecodeTestParams(clz = JavaNested.JavaInnerListener::class, factory = onClickFactory), + BytecodeTestParams(clz = JavaNested.JavaStaticListener::class, factory = onClickFactory), + BytecodeTestParams(clz = KotlinNested.KotlinInnerListener::class, factory = onClickFactory), + BytecodeTestParams(clz = KotlinNested.KotlinStaticListener::class, factory = onClickFactory), + BytecodeTestParams(clz = MissingInterfaceOnClickListener::class, factory = onClickFactory), + BytecodeTestParams(clz = MissingOverrideOnClickListener::class, factory = onClickFactory), + BytecodeTestParams(clz = VirtualMethodRefNamedOnClick::class, factory = onClickFactory), + BytecodeTestParams(clz = JavaLambdaOnClickListener::class, factory = onClickFactory), + BytecodeTestParams.forInnerClass(clz = AnonInnerClassOnClickListener::class, innerClzName = "$1", factory = onClickFactory), + BytecodeTestParams.forInnerClass(clz = JavaAnonOnClickListener::class, innerClzName = "$1", factory = onClickFactory), + BytecodeTestParams.forInnerClass( + clz = KotlinObjectOnClickListener::class, + innerClzName = "\$onCreateView\$1", + factory = onClickFactory + ), + BytecodeTestParams(clz = CustomOnLongClickListener::class, factory = onLongClickFactory), + BytecodeTestParams(clz = ExtendedOnLongClickListener::class, factory = onLongClickFactory), + BytecodeTestParams(clz = JavaLambdaOnLongClickListener::class, factory = onLongClickFactory), + BytecodeTestParams(clz = KotlinNestedOnLongClick.OnLongClickInnerListener::class, factory = onLongClickFactory), + BytecodeTestParams(clz = KotlinNestedOnLongClick.OnLongClickStaticListener::class, factory = onLongClickFactory), + BytecodeTestParams(clz = MissingInterfaceOnLongClickListener::class, factory = onLongClickFactory), + BytecodeTestParams(clz = MissingOverrideOnLongClickListener::class, factory = onLongClickFactory), + BytecodeTestParams.forInnerClass( + clz = AnonInnerClassOnLongClickListener::class, + innerClzName = "$1", + factory = onLongClickFactory + ), + BytecodeTestParams(clz = CustomWebViewClient::class, factory = webviewFactory), + BytecodeTestParams(clz = ExtendedCustomWebViewClient::class, factory = webviewFactory), + BytecodeTestParams(clz = NoOverrideWebViewClient::class, factory = webviewFactory), + ) } @Test diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentedBytecodeTestCases.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentedBytecodeTestCases.kt deleted file mode 100644 index 029944cdd3..0000000000 --- a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/InstrumentedBytecodeTestCases.kt +++ /dev/null @@ -1,134 +0,0 @@ -package io.embrace.gradle.plugin.instrumentation - -import io.embrace.android.gradle.plugin.instrumentation.visitor.OkHttpClassAdapter -import io.embrace.android.gradle.plugin.instrumentation.visitor.OnClickClassAdapter -import io.embrace.android.gradle.plugin.instrumentation.visitor.OnLongClickClassAdapter -import io.embrace.android.gradle.plugin.instrumentation.visitor.WebViewClientClassAdapter -import io.embrace.test.fixtures.ActivityOnClickListener -import io.embrace.test.fixtures.AnonInnerClassOnClickListener -import io.embrace.test.fixtures.AnonInnerClassOnLongClickListener -import io.embrace.test.fixtures.ControlObject -import io.embrace.test.fixtures.CustomOnClickListener -import io.embrace.test.fixtures.CustomOnLongClickListener -import io.embrace.test.fixtures.CustomWebViewClient -import io.embrace.test.fixtures.ExtendedCustomWebViewClient -import io.embrace.test.fixtures.ExtendedOnClickListener -import io.embrace.test.fixtures.ExtendedOnLongClickListener -import io.embrace.test.fixtures.FragmentOnClickListener -import io.embrace.test.fixtures.JavaAnonOnClickListener -import io.embrace.test.fixtures.JavaLambdaOnClickListener -import io.embrace.test.fixtures.JavaLambdaOnLongClickListener -import io.embrace.test.fixtures.JavaNested -import io.embrace.test.fixtures.KotlinNested -import io.embrace.test.fixtures.KotlinNestedOnLongClick -import io.embrace.test.fixtures.KotlinObjectOnClickListener -import io.embrace.test.fixtures.MissingInterfaceOnClickListener -import io.embrace.test.fixtures.MissingInterfaceOnLongClickListener -import io.embrace.test.fixtures.MissingOverrideOnClickListener -import io.embrace.test.fixtures.MissingOverrideOnLongClickListener -import io.embrace.test.fixtures.NoOverrideWebViewClient -import io.embrace.test.fixtures.VirtualMethodRefNamedOnClick -import okhttp3.OkHttpClient -import org.objectweb.asm.ClassVisitor - -private val onClickFactory: ClassVisitorFactory = { visitor -> - OnClickClassAdapter(ASM_API_VERSION, visitor) {} -} - -private val onLongClickFactory: ClassVisitorFactory = { visitor -> - OnLongClickClassAdapter(ASM_API_VERSION, visitor) {} -} - -private val webviewFactory: ClassVisitorFactory = { visitor -> - WebViewClientClassAdapter(ASM_API_VERSION, visitor) {} -} - -private val okHttpFactory: ClassVisitorFactory = { visitor -> - OkHttpClassAdapter(ASM_API_VERSION, visitor) {} -} - -/** - * Declares the test cases for bytecode in [InstrumentedBytecodeTest]. You should define the - * input class, the expected output, and the [ClassVisitor] which will instrument the bytecode. - * - * After doing that, [InstrumentedBytecodeTest] will perform all the necessary checks automatically! - */ -internal fun instrumentedBytecodeTestCases(): List { - return onClickTestCases - .plus(onClickInnerTestCases) - .plus(onLongClickTestCases) - .plus(onLongClickInnerTestCases) - .plus(webclientTestCases) - .plus(okHttpTestCases) - .distinct() // filter out any unintentional duplicate test cases - .sortedBy(BytecodeTestParams::simpleClzName) -} - -private val okHttpTestCases = listOf( - OkHttpClient.Builder::class -).map { - BytecodeTestParams(it.java, factory = okHttpFactory) -} -private val webclientTestCases = listOf( - CustomWebViewClient::class, - ExtendedCustomWebViewClient::class, - NoOverrideWebViewClient::class -).map { - BytecodeTestParams(it.java, factory = webviewFactory) -} - -private val onClickTestCases = listOf( - ActivityOnClickListener::class, - ControlObject::class, - CustomOnClickListener::class, - ExtendedOnClickListener::class, - FragmentOnClickListener::class, - JavaNested.JavaInnerListener::class, - JavaNested.JavaStaticListener::class, - KotlinNested.KotlinInnerListener::class, - KotlinNested.KotlinStaticListener::class, - MissingInterfaceOnClickListener::class, - MissingOverrideOnClickListener::class, - VirtualMethodRefNamedOnClick::class, - JavaLambdaOnClickListener::class, -).map { clz -> - BytecodeTestParams(clz.java, factory = onClickFactory) -} - -private val onClickInnerTestCases = listOf( - BytecodeTestParams.forInnerClass( - AnonInnerClassOnClickListener::class, - "$1", - factory = onClickFactory - ), - BytecodeTestParams.forInnerClass( - JavaAnonOnClickListener::class, - "$1", - factory = onClickFactory - ), - BytecodeTestParams.forInnerClass( - KotlinObjectOnClickListener::class, - "\$onCreateView$1", - factory = onClickFactory - ) -) - -private val onLongClickTestCases = listOf( - CustomOnLongClickListener::class, - ExtendedOnLongClickListener::class, - JavaLambdaOnLongClickListener::class, - KotlinNestedOnLongClick.OnLongClickInnerListener::class, - KotlinNestedOnLongClick.OnLongClickStaticListener::class, - MissingInterfaceOnLongClickListener::class, - MissingOverrideOnLongClickListener::class, -).map { clz -> - BytecodeTestParams(clz.java, factory = onLongClickFactory) -} - -private val onLongClickInnerTestCases = listOf( - BytecodeTestParams.forInnerClass( - AnonInnerClassOnLongClickListener::class, - "$1", - factory = onLongClickFactory - ) -) diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/MethodReturnValueVisitorTest.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/MethodReturnValueVisitorTest.kt index fbba8eab03..c1d91ef951 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/MethodReturnValueVisitorTest.kt +++ b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/MethodReturnValueVisitorTest.kt @@ -30,7 +30,7 @@ class MethodReturnValueVisitorTest { @Test fun `instrument bytecode`() { - val params = BytecodeTestParams(MethodReturnValueVisitorObj::class.java) { nextVisitor -> + val params = BytecodeTestParams(MethodReturnValueVisitorObj::class) { nextVisitor, _ -> TestClassVisitor(ASM_API_VERSION, nextVisitor) } InstrumentationRunner.runInstrumentationAndCompareOutput(params) @@ -74,7 +74,7 @@ class MethodReturnValueVisitorTest { } else if (name == STRING_METHOD_NAME && descriptor == STRING_METHOD_DESCRIPTOR) { return StringReturnValueMethodVisitor("Hello world! I'm a string.", api, nextVisitor) } else if (name == LIST_METHOD_NAME && descriptor == LIST_METHOD_DESCRIPTOR) { - return io.embrace.android.gradle.plugin.instrumentation.config.StringListReturnValueMethodVisitor( + return StringListReturnValueMethodVisitor( listOf("adam aardvark", "bob banana"), api, nextVisitor diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/OkHttpClassFilterTest.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/OkHttpClassFilterTest.kt deleted file mode 100644 index 1d7393a5c5..0000000000 --- a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/OkHttpClassFilterTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.embrace.gradle.plugin.instrumentation - -import io.embrace.android.gradle.plugin.instrumentation.visitor.OkHttpClassAdapter -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test - -class OkHttpClassFilterTest { - - @Test - fun testClassAccepted() { - val ctx = FakeClassContext("okhttp3.OkHttpClient\$Builder") - assertTrue(OkHttpClassAdapter.accept(ctx)) - } - - @Test - fun testClassNotAccepted() { - val ctx = FakeClassContext("okhttp3.OkHttpClient") - assertFalse(OkHttpClassAdapter.accept(ctx)) - } -} diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/OnClickClassFilterTest.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/OnClickClassFilterTest.kt deleted file mode 100644 index de82b2d820..0000000000 --- a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/OnClickClassFilterTest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.embrace.gradle.plugin.instrumentation - -import io.embrace.android.gradle.plugin.instrumentation.visitor.OnClickClassAdapter -import org.junit.Assert.assertTrue -import org.junit.Test - -class OnClickClassFilterTest { - - @Test - fun testClassAccepted() { - val ctx = FakeClassContext("org/test/FooBar") - assertTrue(OnClickClassAdapter.accept(ctx)) - } -} diff --git a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/WebViewClassFilterTest.kt b/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/WebViewClassFilterTest.kt deleted file mode 100644 index 56e999e512..0000000000 --- a/embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/WebViewClassFilterTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.embrace.gradle.plugin.instrumentation - -import io.embrace.android.gradle.plugin.instrumentation.visitor.WebViewClientClassAdapter -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test - -class WebViewClassFilterTest { - - @Test - fun testClassAccepted() { - val ctx = FakeClassContext( - FakeClassData( - "", - superClasses = listOf("android.webkit.WebViewClient") - ) - ) - assertTrue(WebViewClientClassAdapter.accept(ctx)) - } - - @Test - fun testClassNotAccepted() { - val ctx = FakeClassContext( - FakeClassData( - "", - superClasses = emptyList() - ) - ) - assertFalse(WebViewClientClassAdapter.accept(ctx)) - } -} diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/ActivityOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/ActivityOnClickListener_expected.txt index 960ea3da79..0d3f0dc607 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/ActivityOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/ActivityOnClickListener_expected.txt @@ -20,9 +20,8 @@ public class io/embrace/test/fixtures/ActivityOnClickListener extends androidx/a // access flags 0x1 public onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 14 L0 RETURN diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/AnonInnerClassOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/AnonInnerClassOnClickListener_expected.txt index 96c8748be1..8a7eb82ee8 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/AnonInnerClassOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/AnonInnerClassOnClickListener_expected.txt @@ -31,9 +31,8 @@ class io/embrace/test/fixtures/AnonInnerClassOnClickListener$1 implements androi // access flags 0x1 public onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 14 L0 RETURN diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/AnonInnerClassOnLongClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/AnonInnerClassOnLongClickListener_expected.txt index 679e21da83..c9aff02571 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/AnonInnerClassOnLongClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/AnonInnerClassOnLongClickListener_expected.txt @@ -31,9 +31,8 @@ class io/embrace/test/fixtures/AnonInnerClassOnLongClickListener$1 implements an // access flags 0x1 public onLongClick(Landroid/view/View;)Z - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 LINENUMBER 13 L0 ICONST_0 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/Builder_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/Builder_expected.txt index dbe58cf7f2..ed4f2abe94 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/Builder_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/Builder_expected.txt @@ -3380,7 +3380,7 @@ kotlin/jvm/internal/FakeKt public final build()Lokhttp3/OkHttpClient; @Lorg/jetbrains/annotations/NotNull;() // invisible ALOAD 0 - INVOKESTATIC io/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient$Builder._preBuild (Lokhttp3/OkHttpClient$Builder;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OkHttpBytecodeEntrypoint.build (Lokhttp3/OkHttpClient$Builder;)V L0 LINENUMBER 1069 L0 NEW okhttp3/OkHttpClient diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/CustomOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/CustomOnClickListener_expected.txt index c5c98e969f..2f3eacef25 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/CustomOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/CustomOnClickListener_expected.txt @@ -24,9 +24,8 @@ public class io/embrace/test/fixtures/CustomOnClickListener implements android/v public onClick(Landroid/view/View;)V // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 ALOAD 1 LDC "view" diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/CustomOnLongClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/CustomOnLongClickListener_expected.txt index 593f8d7403..a450ac7261 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/CustomOnLongClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/CustomOnLongClickListener_expected.txt @@ -24,9 +24,8 @@ public class io/embrace/test/fixtures/CustomOnLongClickListener implements andro public onLongClick(Landroid/view/View;)Z // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 LINENUMBER 11 L0 ALOAD 1 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/CustomWebViewClient_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/CustomWebViewClient_expected.txt index a62fc6a188..11ff463a10 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/CustomWebViewClient_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/CustomWebViewClient_expected.txt @@ -24,10 +24,8 @@ public class io/embrace/test/fixtures/CustomWebViewClient extends android/webkit @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 1 @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 2 - ALOAD 1 ALOAD 2 - ALOAD 3 - INVOKESTATIC io/embrace/android/embracesdk/WebViewClientSwazzledHooks._preOnPageStarted (Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint.onPageStarted (Ljava/lang/String;)V L0 LINENUMBER 10 L0 ALOAD 0 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedCustomWebViewClient_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedCustomWebViewClient_expected.txt index e528f1d770..7a4996c4a2 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedCustomWebViewClient_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedCustomWebViewClient_expected.txt @@ -24,10 +24,8 @@ public final class io/embrace/test/fixtures/ExtendedCustomWebViewClient extends @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 1 @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 2 - ALOAD 1 ALOAD 2 - ALOAD 3 - INVOKESTATIC io/embrace/android/embracesdk/WebViewClientSwazzledHooks._preOnPageStarted (Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint.onPageStarted (Ljava/lang/String;)V L0 LINENUMBER 8 L0 ALOAD 0 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedOnClickListener_expected.txt index 47aa4f7241..c9cb791419 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedOnClickListener_expected.txt @@ -20,9 +20,8 @@ public class io/embrace/test/fixtures/ExtendedOnClickListener extends io/embrace public onClick(Landroid/view/View;)V // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 16 L0 ALOAD 0 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedOnLongClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedOnLongClickListener_expected.txt index ab59254080..d7dd9b8be6 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedOnLongClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/ExtendedOnLongClickListener_expected.txt @@ -20,9 +20,8 @@ public class io/embrace/test/fixtures/ExtendedOnLongClickListener extends io/emb public onLongClick(Landroid/view/View;)Z // annotable parameter count: 1 (invisible) @Landroidx/annotation/Nullable;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 LINENUMBER 17 L0 ALOAD 1 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/FragmentOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/FragmentOnClickListener_expected.txt index 1c336d93a5..9ed379c607 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/FragmentOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/FragmentOnClickListener_expected.txt @@ -26,9 +26,8 @@ public final class io/embrace/test/fixtures/FragmentOnClickListener extends andr public onClick(Landroid/view/View;)V // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 ALOAD 1 LDC "view" diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaAnonOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaAnonOnClickListener_expected.txt index 5ffa8d6ff0..1216e9d0f4 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaAnonOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaAnonOnClickListener_expected.txt @@ -31,9 +31,8 @@ class io/embrace/test/fixtures/JavaAnonOnClickListener$1 implements android/view // access flags 0x1 public onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 25 L0 LDC "Embrace" diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaInnerListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaInnerListener_expected.txt index 2a91b24faf..503132c372 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaInnerListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaInnerListener_expected.txt @@ -30,9 +30,8 @@ public class io/embrace/test/fixtures/JavaNested$JavaInnerListener implements an // access flags 0x1 public onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 15 L0 RETURN diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaLambdaOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaLambdaOnClickListener_expected.txt index 015abdecb2..251d0fca08 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaLambdaOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaLambdaOnClickListener_expected.txt @@ -65,9 +65,6 @@ public class io/embrace/test/fixtures/JavaLambdaOnClickListener extends androidx // access flags 0x100A private static synthetic lambda$onCreateView$0(Landroid/view/View;)V - ACONST_NULL - ALOAD 0 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V L0 LINENUMBER 22 L0 LDC "Embrace" diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaLambdaOnLongClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaLambdaOnLongClickListener_expected.txt index 4dea0c5f0a..ef12ed6c6f 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaLambdaOnLongClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaLambdaOnLongClickListener_expected.txt @@ -65,9 +65,6 @@ public class io/embrace/test/fixtures/JavaLambdaOnLongClickListener extends andr // access flags 0x100A private static synthetic lambda$onCreateView$0(Landroid/view/View;)Z - ACONST_NULL - ALOAD 0 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V L0 LINENUMBER 22 L0 LDC "Embrace" diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaStaticListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaStaticListener_expected.txt index d515cafae0..1a2efcdd74 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/JavaStaticListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/JavaStaticListener_expected.txt @@ -23,9 +23,8 @@ public class io/embrace/test/fixtures/JavaNested$JavaStaticListener implements a // access flags 0x1 public onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 22 L0 RETURN diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinInnerListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinInnerListener_expected.txt index 690d89a6f5..6c64da69bf 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinInnerListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinInnerListener_expected.txt @@ -35,9 +35,8 @@ public final class io/embrace/test/fixtures/KotlinNested$KotlinInnerListener imp public onClick(Landroid/view/View;)V // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 ALOAD 1 LDC "view" diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinLambdaOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinLambdaOnClickListener_expected.txt index 621424d627..e024b6230b 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinLambdaOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinLambdaOnClickListener_expected.txt @@ -27,7 +27,7 @@ public final class io/embrace/test/fixtures/KotlinObjectOnClickListener$onCreate @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 20 L0 RETURN diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinLambdaOnLongClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinLambdaOnLongClickListener_expected.txt index 7e70f9dcae..9b740d3f48 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinLambdaOnLongClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinLambdaOnLongClickListener_expected.txt @@ -16,7 +16,7 @@ final class io/embrace/test/fixtures/KotlinLambdaOnLongClickListener$onCreateVie public final onLongClick(Landroid/view/View;)Z ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 LINENUMBER 18 L0 ICONST_0 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRef2OnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRef2OnClickListener_expected.txt index 3f1b43ad6d..080eded371 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRef2OnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRef2OnClickListener_expected.txt @@ -24,7 +24,7 @@ final class io/embrace/test/fixtures/KotlinMethodRef2OnClickListener$sam$android public final synthetic onClick(Landroid/view/View;)V ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 ALOAD 0 GETFIELD io/embrace/test/fixtures/KotlinMethodRef2OnClickListener$sam$android_view_View_OnClickListener$0.function : Lkotlin/jvm/functions/Function1; diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRefOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRefOnClickListener_expected.txt index 6faa88a798..6cc2f4da34 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRefOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRefOnClickListener_expected.txt @@ -24,7 +24,7 @@ final class io/embrace/test/fixtures/KotlinMethodRefOnClickListener$sam$android_ public final synthetic onClick(Landroid/view/View;)V ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 ALOAD 0 GETFIELD io/embrace/test/fixtures/KotlinMethodRefOnClickListener$sam$android_view_View_OnClickListener$0.function : Lkotlin/jvm/functions/Function1; diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRefOnLongClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRefOnLongClickListener_expected.txt index 7d89a000c6..ccd07a1052 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRefOnLongClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinMethodRefOnLongClickListener_expected.txt @@ -24,7 +24,7 @@ final class io/embrace/test/fixtures/KotlinMethodRefOnLongClickListener$sam$andr public final synthetic onLongClick(Landroid/view/View;)Z ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 ALOAD 0 GETFIELD io/embrace/test/fixtures/KotlinMethodRefOnLongClickListener$sam$android_view_View_OnLongClickListener$0.function : Lkotlin/jvm/functions/Function1; diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinObjectOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinObjectOnClickListener_expected.txt index 6163eca7db..b1073bb2cf 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinObjectOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinObjectOnClickListener_expected.txt @@ -25,9 +25,8 @@ public final class io/embrace/test/fixtures/KotlinObjectOnClickListener$onCreate // access flags 0x1 public onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 19 L0 RETURN diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinStaticListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinStaticListener_expected.txt index b6b0fbd4e7..8d8c515039 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinStaticListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/KotlinStaticListener_expected.txt @@ -26,9 +26,8 @@ public final class io/embrace/test/fixtures/KotlinNested$KotlinStaticListener im public onClick(Landroid/view/View;)V // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 ALOAD 1 LDC "view" diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/MissingInterfaceOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/MissingInterfaceOnClickListener_expected.txt index 1832e100dd..2f7dc83e18 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/MissingInterfaceOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/MissingInterfaceOnClickListener_expected.txt @@ -18,9 +18,8 @@ public class io/embrace/test/fixtures/MissingInterfaceOnClickListener { // access flags 0x1 public onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 11 L0 RETURN diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/MissingInterfaceOnLongClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/MissingInterfaceOnLongClickListener_expected.txt index 2b608e943f..998d17c080 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/MissingInterfaceOnLongClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/MissingInterfaceOnLongClickListener_expected.txt @@ -18,9 +18,8 @@ public class io/embrace/test/fixtures/MissingInterfaceOnLongClickListener { // access flags 0x1 public onLongClick(Landroid/view/View;)Z - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 LINENUMBER 10 L0 ICONST_1 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/MissingOverrideOnClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/MissingOverrideOnClickListener_expected.txt index 1ce673c9a2..de43a69d27 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/MissingOverrideOnClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/MissingOverrideOnClickListener_expected.txt @@ -20,9 +20,8 @@ public class io/embrace/test/fixtures/MissingOverrideOnClickListener implements // access flags 0x1 public onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 11 L0 RETURN diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/MissingOverrideOnLongClickListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/MissingOverrideOnLongClickListener_expected.txt index 4e048fd4dd..654af50b34 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/MissingOverrideOnLongClickListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/MissingOverrideOnLongClickListener_expected.txt @@ -20,9 +20,8 @@ public class io/embrace/test/fixtures/MissingOverrideOnLongClickListener impleme // access flags 0x1 public onLongClick(Landroid/view/View;)Z - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 LINENUMBER 10 L0 ICONST_0 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/NoOverrideWebViewClient_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/NoOverrideWebViewClient_expected.txt index 87f0838f74..a0a6b37057 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/NoOverrideWebViewClient_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/NoOverrideWebViewClient_expected.txt @@ -20,15 +20,13 @@ public final class io/embrace/test/fixtures/NoOverrideWebViewClient extends andr // access flags 0x1 public onPageStarted(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V - ALOAD 1 - ALOAD 2 - ALOAD 3 - INVOKESTATIC io/embrace/android/embracesdk/WebViewClientSwazzledHooks._preOnPageStarted (Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V ALOAD 0 ALOAD 1 ALOAD 2 ALOAD 3 INVOKESPECIAL android/webkit/WebViewClient.onPageStarted (Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V + ALOAD 2 + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint.onPageStarted (Ljava/lang/String;)V RETURN MAXSTACK = 4 MAXLOCALS = 0 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/OnLongClickInnerListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/OnLongClickInnerListener_expected.txt index 255d27e6b1..3bb6317a63 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/OnLongClickInnerListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/OnLongClickInnerListener_expected.txt @@ -35,9 +35,8 @@ public final class io/embrace/test/fixtures/KotlinNestedOnLongClick$OnLongClickI public onLongClick(Landroid/view/View;)Z // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 LINENUMBER 11 L0 ICONST_1 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/OnLongClickStaticListener_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/OnLongClickStaticListener_expected.txt index 56b4834c1b..c07deaeb41 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/OnLongClickStaticListener_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/OnLongClickStaticListener_expected.txt @@ -26,9 +26,8 @@ public final class io/embrace/test/fixtures/KotlinNestedOnLongClick$OnLongClickS public onLongClick(Landroid/view/View;)Z // annotable parameter count: 1 (invisible) @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener._preOnLongClick (Landroid/view/View$OnLongClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint.onLongClick (Landroid/view/View;)V L0 LINENUMBER 17 L0 ICONST_1 diff --git a/embrace-bytecode-instrumentation-tests/src/test/resources/VirtualMethodRefNamedOnClick_expected.txt b/embrace-bytecode-instrumentation-tests/src/test/resources/VirtualMethodRefNamedOnClick_expected.txt index d476161785..4e89a3bed1 100644 --- a/embrace-bytecode-instrumentation-tests/src/test/resources/VirtualMethodRefNamedOnClick_expected.txt +++ b/embrace-bytecode-instrumentation-tests/src/test/resources/VirtualMethodRefNamedOnClick_expected.txt @@ -22,9 +22,8 @@ public class io/embrace/test/fixtures/VirtualMethodRefNamedOnClick extends andro // access flags 0x2 private onClick(Landroid/view/View;)V - ALOAD 0 ALOAD 1 - INVOKESTATIC io/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener._preOnClick (Landroid/view/View$OnClickListener;Landroid/view/View;)V + INVOKESTATIC io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint.onClick (Landroid/view/View;)V L0 LINENUMBER 21 L0 LDC "Embrace" diff --git a/embrace-gradle-plugin-integration-tests/build.gradle.kts b/embrace-gradle-plugin-integration-tests/build.gradle.kts index 47d80b161a..52a921e69a 100644 --- a/embrace-gradle-plugin-integration-tests/build.gradle.kts +++ b/embrace-gradle-plugin-integration-tests/build.gradle.kts @@ -38,6 +38,7 @@ tasks.withType(Test::class.java).configureEach { ":embrace-android-sdk", ":embrace-android-core", ":embrace-android-api", + ":embrace-android-fcm", ":embrace-android-okhttp3", ":embrace-android-infra", ":embrace-android-features", diff --git a/embrace-gradle-plugin-integration-tests/fixtures/android-instrumentation/build.gradle b/embrace-gradle-plugin-integration-tests/fixtures/android-instrumentation/build.gradle index 85fea8403d..4fcbfa1df6 100644 --- a/embrace-gradle-plugin-integration-tests/fixtures/android-instrumentation/build.gradle +++ b/embrace-gradle-plugin-integration-tests/fixtures/android-instrumentation/build.gradle @@ -49,6 +49,7 @@ android { dependencies { implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.google.firebase:firebase-messaging:23.1.0") + implementation("io.embrace:embrace-android-sdk:+") implementation("io.embrace:embrace-android-fcm:+") } diff --git a/embrace-gradle-plugin-integration-tests/fixtures/android-instrumentation/proguard-rules.pro b/embrace-gradle-plugin-integration-tests/fixtures/android-instrumentation/proguard-rules.pro index bf47913286..545d7c3e16 100644 --- a/embrace-gradle-plugin-integration-tests/fixtures/android-instrumentation/proguard-rules.pro +++ b/embrace-gradle-plugin-integration-tests/fixtures/android-instrumentation/proguard-rules.pro @@ -1,6 +1,3 @@ -keep class com.example.app.** { *; } -keep class okhttp3.** { *; } --keep class io.embrace.android.embracesdk.internal.config.instrumented.** { *; } --keep class io.embrace.android.embracesdk.ViewSwazzledHooks$OnClickListener { *; } --keep class io.embrace.android.embracesdk.ViewSwazzledHooks$OnLongClickListener { *; } -keep class com.google.firebase.messaging.RemoteMessage diff --git a/embrace-gradle-plugin-integration-tests/src/test/resources/bytecode-instrumentation-enabled.json b/embrace-gradle-plugin-integration-tests/src/test/resources/bytecode-instrumentation-enabled.json index 665ba81d1c..50d3596dc6 100644 --- a/embrace-gradle-plugin-integration-tests/src/test/resources/bytecode-instrumentation-enabled.json +++ b/embrace-gradle-plugin-integration-tests/src/test/resources/bytecode-instrumentation-enabled.json @@ -5,7 +5,7 @@ "methods": [ { "signature": "onPageStarted(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V", - "embraceHook": "invoke-static {p1, p2, p3}, Lio/embrace/android/embracesdk/WebViewClientSwazzledHooks;->_preOnPageStarted(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V" + "embraceHook": "invoke-static {p2}, Lio/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint;->onPageStarted(Ljava/lang/String;)V" } ] }, @@ -14,7 +14,7 @@ "methods": [ { "signature": "onClick(Landroid/view/View;)V", - "embraceHook": "invoke-static {p0, p1}, Lio/embrace/android/embracesdk/ViewSwazzledHooks$OnClickListener;->_preOnClick(Landroid/view/View$OnClickListener;Landroid/view/View;)V" + "embraceHook": "invoke-static {p1}, Lio/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint;->onClick(Landroid/view/View;)V" } ] }, @@ -23,7 +23,7 @@ "methods": [ { "signature": "onLongClick(Landroid/view/View;)Z", - "embraceHook": "invoke-static {p0, p1}, Lio/embrace/android/embracesdk/ViewSwazzledHooks$OnLongClickListener;->_preOnLongClick(Landroid/view/View$OnLongClickListener;Landroid/view/View;)V", + "embraceHook": "invoke-static {p1}, Lio/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint;->onLongClick(Landroid/view/View;)V", "returnValue": "false" } ] @@ -33,7 +33,7 @@ "methods": [ { "signature": "build()Lokhttp3/OkHttpClient;", - "embraceHook": "invoke-static {p0}, Lio/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient$Builder;->_preBuild(Lokhttp3/OkHttpClient$Builder;)V" + "embraceHook": "invoke-static {p0}, Lio/embrace/android/embracesdk/internal/instrumentation/bytecode/OkHttpBytecodeEntrypoint;->build(Lokhttp3/OkHttpClient$Builder;)V" } ] }, @@ -42,7 +42,7 @@ "methods": [ { "signature": "onMessageReceived(Lcom/google/firebase/messaging/RemoteMessage;)V", - "embraceHook": "invoke-static {p1}, Lio/embrace/android/embracesdk/fcm/swazzle/callback/com/android/fcm/FirebaseSwazzledHooks;->_onMessageReceived(Lcom/google/firebase/messaging/RemoteMessage;)V" + "embraceHook": "invoke-static {p1}, Lio/embrace/android/embracesdk/internal/instrumentation/bytecode/FcmBytecodeEntrypoint;->onMessageReceived(Lcom/google/firebase/messaging/RemoteMessage;)V" } ] } diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/EmbraceClassVisitorFactory.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/EmbraceClassVisitorFactory.kt index 9821619c2c..5eda5b939f 100644 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/EmbraceClassVisitorFactory.kt +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/EmbraceClassVisitorFactory.kt @@ -3,7 +3,8 @@ package io.embrace.android.gradle.plugin.instrumentation import com.android.build.api.instrumentation.AsmClassVisitorFactory import com.android.build.api.instrumentation.ClassContext import com.android.build.api.instrumentation.ClassData -import io.embrace.android.gradle.plugin.Logger +import io.embrace.android.gradle.plugin.instrumentation.config.ConfigClassVisitorFactory +import io.embrace.android.gradle.plugin.instrumentation.json.readBytecodeInstrumentationFeatures import org.objectweb.asm.ClassVisitor /** @@ -18,16 +19,27 @@ abstract class EmbraceClassVisitorFactory : AsmClassVisitorFactory + visitor = factory.createClassVisitor(feature, classContext, visitor) + } + return visitor } override fun isInstrumentable(classData: ClassData): Boolean { @@ -39,12 +51,6 @@ abstract class EmbraceClassVisitorFactory : AsmClassVisitorFactory, - logger: (() -> String) -> Unit, -): ClassVisitor { - val api = instrumentationContext.apiVersion.get() - var visitor = nextClassVisitor - val className = classContext.currentClassData.className - - // Add a visitor if this is a config class provided by the SDK - val cfg = parameters.get().config.get() - val encodedSharedObjectFilesMap = parameters.get().encodedSharedObjectFilesMap.orNull - ConfigClassVisitorFactory.createClassVisitor(className, cfg, encodedSharedObjectFilesMap, api, visitor)?.let { - visitor = it - } - - // chain our own visitors to avoid unlikely (but possible) cases such as a custom - // WebViewClient implementing an OnClickListener - if (parameters.get().shouldInstrumentFirebaseMessaging.get() && - FirebaseMessagingServiceClassAdapter.accept(classContext) - ) { - visitor = FirebaseMessagingServiceClassAdapter(api, visitor, logger) - logger { "Added FirebaseMessagingServiceClassAdapter for $className." } - } - - if (parameters.get().shouldInstrumentWebview.get() && WebViewClientClassAdapter.accept(classContext)) { - visitor = WebViewClientClassAdapter(api, visitor, logger) - logger { "Added WebViewClientClassAdapter for $className." } - } - if (parameters.get().shouldInstrumentOkHttp.get() && OkHttpClassAdapter.accept(classContext)) { - visitor = OkHttpClassAdapter(api, visitor, logger) - logger { "Added OkHttpClassAdapter for $className." } - } - if (parameters.get().shouldInstrumentOnLongClick.get() && OnLongClickClassAdapter.accept(classContext)) { - visitor = OnLongClickClassAdapter(api, visitor, logger) - logger { "Added OnLongClickClassAdapter for $className." } - } - if (parameters.get().shouldInstrumentOnClick.get() && OnClickClassAdapter.accept(classContext)) { - visitor = OnClickClassAdapter(api, visitor, logger) - logger { "Added OnClickClassAdapter for $className." } - } - return visitor -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/TransformClassesWithReflectionException.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/TransformClassesWithReflectionException.kt deleted file mode 100644 index 516b2db1e3..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/TransformClassesWithReflectionException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation - -class TransformClassesWithReflectionException(message: String?) : Exception(message) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/VisitorFactoryImpl.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/VisitorFactoryImpl.kt new file mode 100644 index 0000000000..0b77f9f0e6 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/VisitorFactoryImpl.kt @@ -0,0 +1,52 @@ +package io.embrace.android.gradle.plugin.instrumentation + +import com.android.build.api.instrumentation.ClassContext +import io.embrace.android.gradle.plugin.instrumentation.visitor.BytecodeInstrumentationFeature +import io.embrace.android.gradle.plugin.instrumentation.visitor.InsertSuperOverrideClassVisitor +import io.embrace.android.gradle.plugin.instrumentation.visitor.InstrumentationTargetClassVisitor +import org.objectweb.asm.ClassVisitor + +class VisitorFactoryImpl( + private val api: Int, + private val params: BytecodeInstrumentationParams, +) { + + fun createClassVisitor( + feature: BytecodeInstrumentationFeature, + classContext: ClassContext, + nextVisitor: ClassVisitor, + ): ClassVisitor { + if (feature.isEnabled(params, classContext)) { + return if (feature.addOverrideParams != null) { + // if specified, add an override for a method if it doesn't already exist in the class + InsertSuperOverrideClassVisitor( + api = api, + nextClassVisitor = nextVisitor, + feature = feature, + ) + } else { + InstrumentationTargetClassVisitor( + api = api, + nextClassVisitor = nextVisitor, + feature = feature, + ) + } + } + return nextVisitor + } + + private fun BytecodeInstrumentationFeature.isEnabled( + params: BytecodeInstrumentationParams, + ctx: ClassContext, + ): Boolean { + val enabledViaDsl = when (name) { + "fcm_push_notifications" -> params.shouldInstrumentFirebaseMessaging.get() + "okhttp" -> params.shouldInstrumentOkHttp.get() + "webview_page_start" -> params.shouldInstrumentWebview.get() + "on_click" -> params.shouldInstrumentOnClick.get() + "on_long_click" -> params.shouldInstrumentOnLongClick.get() + else -> error("Unknown feature: $name. Please add a property that enables/disables it on EmbraceBytecodeInstrumentation.") + } + return enabledViaDsl && visitStrategy.shouldVisit(ctx) + } +} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigAddOverride.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigAddOverride.kt new file mode 100644 index 0000000000..189a10983f --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigAddOverride.kt @@ -0,0 +1,10 @@ +package io.embrace.android.gradle.plugin.instrumentation.json + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class InstrumentationConfigAddOverride( + val owner: String, + val name: String, + val descriptor: String, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeature.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeature.kt new file mode 100644 index 0000000000..12c988945f --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeature.kt @@ -0,0 +1,12 @@ +package io.embrace.android.gradle.plugin.instrumentation.json + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class InstrumentationConfigFeature( + val name: String, + val target: InstrumentationConfigTarget, + val insert: InstrumentationConfigInsert, + val visitStrategy: InstrumentationConfigVisitStrategy, + val addOverride: InstrumentationConfigAddOverride?, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeatures.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeatures.kt new file mode 100644 index 0000000000..03e966643a --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeatures.kt @@ -0,0 +1,8 @@ +package io.embrace.android.gradle.plugin.instrumentation.json + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class InstrumentationConfigFeatures( + val features: List, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeaturesReader.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeaturesReader.kt new file mode 100644 index 0000000000..af59ec2dc2 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigFeaturesReader.kt @@ -0,0 +1,59 @@ +package io.embrace.android.gradle.plugin.instrumentation.json + +import com.squareup.moshi.Moshi +import io.embrace.android.gradle.plugin.instrumentation.strategy.ClassVisitStrategy +import io.embrace.android.gradle.plugin.instrumentation.visitor.BytecodeClassInsertionParams +import io.embrace.android.gradle.plugin.instrumentation.visitor.BytecodeInstrumentationFeature +import io.embrace.android.gradle.plugin.instrumentation.visitor.BytecodeMethodInsertionParams +import io.embrace.android.gradle.plugin.instrumentation.visitor.BytecodeMethodOverrideParams +import okio.buffer +import okio.source + +fun readBytecodeInstrumentationFeatures(): List { + val configFeatures = readBytecodeInstrumentationConfig() + return configFeatures.features.map(InstrumentationConfigFeature::convertInstrumentationConfigFeature) +} + +private fun readBytecodeInstrumentationConfig(): InstrumentationConfigFeatures { + val classLoader = InstrumentationConfigFeatures::class.java.classLoader + val stream = classLoader.getResourceAsStream("bytecode_instrumentation_features.json") + ?: error("Bytecode instrumentation config file not found") + + return stream.use { + val moshi = Moshi.Builder().build() + val adapter = moshi.adapter(InstrumentationConfigFeatures::class.java) + adapter.fromJson(it.source().buffer()) + } ?: error("Failed to parse bytecode instrumentation config file") +} + +private fun InstrumentationConfigFeature.convertInstrumentationConfigFeature(): BytecodeInstrumentationFeature = + BytecodeInstrumentationFeature( + name = name, + targetParams = BytecodeClassInsertionParams( + name = target.name, + descriptor = target.descriptor + ), + insertionParams = BytecodeMethodInsertionParams( + owner = insert.owner, + name = insert.name, + descriptor = insert.descriptor, + operandStackIndices = insert.operandStackIndices, + ), + visitStrategy = convertVisitStrategy(), + addOverrideParams = addOverride?.let { + BytecodeMethodOverrideParams( + owner = it.owner, + name = it.name, + descriptor = it.descriptor, + ) + } + ) + +private fun InstrumentationConfigFeature.convertVisitStrategy(): ClassVisitStrategy { + return when (visitStrategy.type) { + "match_super_class_name" -> ClassVisitStrategy.MatchSuperClassName(checkNotNull(visitStrategy.value)) + "match_class_name" -> ClassVisitStrategy.MatchClassName(checkNotNull(visitStrategy.value)) + "exhaustive" -> ClassVisitStrategy.Exhaustive + else -> error("Unsupported visit strategy type: ${visitStrategy.type}") + } +} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigInsert.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigInsert.kt new file mode 100644 index 0000000000..3f7c665641 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigInsert.kt @@ -0,0 +1,11 @@ +package io.embrace.android.gradle.plugin.instrumentation.json + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class InstrumentationConfigInsert( + val owner: String, + val name: String, + val descriptor: String, + val operandStackIndices: List, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigTarget.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigTarget.kt new file mode 100644 index 0000000000..51428e5170 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigTarget.kt @@ -0,0 +1,9 @@ +package io.embrace.android.gradle.plugin.instrumentation.json + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class InstrumentationConfigTarget( + val name: String, + val descriptor: String, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigVisitStrategy.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigVisitStrategy.kt new file mode 100644 index 0000000000..55b4a05ac6 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/json/InstrumentationConfigVisitStrategy.kt @@ -0,0 +1,9 @@ +package io.embrace.android.gradle.plugin.instrumentation.json + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class InstrumentationConfigVisitStrategy( + val type: String, + val value: String? = null, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/strategy/ClassVisitStrategy.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/strategy/ClassVisitStrategy.kt new file mode 100644 index 0000000000..2e73ec27ff --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/strategy/ClassVisitStrategy.kt @@ -0,0 +1,40 @@ +package io.embrace.android.gradle.plugin.instrumentation.strategy + +import com.android.build.api.instrumentation.ClassContext + +/** + * Defines all the potential strategies for deciding whether a class should be visited by a particular feature + * during bytecode instrumentation. + */ +sealed class ClassVisitStrategy { + + /** + * Returns true if the class should be visited for _potential_ instrumentation. + */ + abstract fun shouldVisit(ctx: ClassContext): Boolean + + /** + * Visits every class. + */ + internal object Exhaustive : ClassVisitStrategy() { + override fun shouldVisit(ctx: ClassContext): Boolean = true + } + + /** + * Visits every class matching the given name. + */ + internal class MatchClassName(private val name: String) : ClassVisitStrategy() { + override fun shouldVisit(ctx: ClassContext): Boolean { + return ctx.currentClassData.className == name + } + } + + /** + * Visits every class that has a superclass matching the given name. + */ + internal class MatchSuperClassName(private val name: String) : ClassVisitStrategy() { + override fun shouldVisit(ctx: ClassContext): Boolean { + return ctx.currentClassData.superClasses.contains(name) + } + } +} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeClassInsertionParams.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeClassInsertionParams.kt new file mode 100644 index 0000000000..3a407806e7 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeClassInsertionParams.kt @@ -0,0 +1,17 @@ +package io.embrace.android.gradle.plugin.instrumentation.visitor + +/** + * Parameters used to detect that a method in a class is a target for bytecode instrumentation. + */ +data class BytecodeClassInsertionParams( + + /** + * The function name that will be targeted for bytecode instrumentation. + */ + val name: String, + + /** + * The type signature of the function that will be targeted for bytecode instrumentation. + */ + val descriptor: String, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeInstrumentationFeature.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeInstrumentationFeature.kt new file mode 100644 index 0000000000..c85de519b2 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeInstrumentationFeature.kt @@ -0,0 +1,34 @@ +package io.embrace.android.gradle.plugin.instrumentation.visitor + +import io.embrace.android.gradle.plugin.instrumentation.strategy.ClassVisitStrategy + +/** + * Declares all the necessary information for performing bytecode instrumentation on a specific feature. + */ +data class BytecodeInstrumentationFeature( + + /** + * The name of the feature. + */ + val name: String, + + /** + * Instructions for identifying a target method in the class that will be instrumented. + */ + val targetParams: BytecodeClassInsertionParams, + + /** + * Instructions for how to insert bytecode into the target method. + */ + val insertionParams: BytecodeMethodInsertionParams, + + /** + * Instructions for determining whether a class should be visited for instrumentation for this feature. + */ + val visitStrategy: ClassVisitStrategy, + + /** + * Instructions for how to insert an override into a class that calls to super. + */ + val addOverrideParams: BytecodeMethodOverrideParams? = null, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeMethodInsertionParams.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeMethodInsertionParams.kt new file mode 100644 index 0000000000..976e832da5 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeMethodInsertionParams.kt @@ -0,0 +1,34 @@ +package io.embrace.android.gradle.plugin.instrumentation.visitor + +/** + * The parameters that should be used to insert a function call into the body of a method. + */ +data class BytecodeMethodInsertionParams( + + /** + * The fully qualified class name containing the method to be instrumented. + */ + val owner: String, + + /** + * The function name that will be instrumented. + */ + val name: String, + + /** + * The type signature of the function to be instrumented. + */ + val descriptor: String, + + /** + * Declares the indices of variables on the operand stack that should be added as part of the inserted call. + * This MUST be entered in ascending order. + * + * Virtual methods have an implicit reference as the 0th value on the stack, whereas static methods do not. + * After this point, the stack should contain the ordered arguments to the containing method. + * + * As an example, listOf(1, 3) should be supplied if we wanted to pass the String and Boolean parameters of this virtual method: + * foo(String, Int, Boolean) + */ + val operandStackIndices: List, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeMethodOverrideParams.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeMethodOverrideParams.kt new file mode 100644 index 0000000000..6f5571888a --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/BytecodeMethodOverrideParams.kt @@ -0,0 +1,22 @@ +package io.embrace.android.gradle.plugin.instrumentation.visitor + +/** + * The parameters that should be used to insert an override into a class that calls to super. + */ +data class BytecodeMethodOverrideParams( + + /** + * The fully qualified class name containing the method to be instrumented. + */ + val owner: String, + + /** + * The function name that will be instrumented. + */ + val name: String, + + /** + * The type signature of the function to be instrumented. + */ + val descriptor: String, +) diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/ClassVisitFilter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/ClassVisitFilter.kt deleted file mode 100644 index a4446ed1ed..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/ClassVisitFilter.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import com.android.build.api.instrumentation.ClassContext - -@Suppress("UnstableApiUsage") -interface ClassVisitFilter { - - /** - * Returns true if a visitor wants to visit a given class context. - */ - fun accept(classContext: ClassContext): Boolean -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/FirebaseMessagingServiceClassAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/FirebaseMessagingServiceClassAdapter.kt deleted file mode 100644 index a5b7b47d63..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/FirebaseMessagingServiceClassAdapter.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import com.android.build.api.instrumentation.ClassContext -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.MethodVisitor - -/** - * Visits the [FirebaseMessagingService] class and returns a [FirebaseMessagingServiceMethodAdapter] - * for the onMessageReceived method. - */ -class FirebaseMessagingServiceClassAdapter( - api: Int, - internal val nextClassVisitor: ClassVisitor?, - private val logger: (() -> String) -> Unit -) : ClassVisitor(api, nextClassVisitor) { - - companion object : ClassVisitFilter { - private const val CLASS_NAME = "com.google.firebase.messaging.FirebaseMessagingService" - private const val METHOD_NAME = "onMessageReceived" - private const val METHOD_DESC = "(Lcom/google/firebase/messaging/RemoteMessage;)V" - - @Suppress("UnstableApiUsage") - override fun accept(classContext: ClassContext): Boolean { - if (classContext.currentClassData.superClasses.contains(CLASS_NAME)) { - return true - } - return false - } - } - override fun visitMethod( - access: Int, - name: String, - desc: String, - signature: String?, - exceptions: Array? - ): MethodVisitor? { - val nextMethodVisitor = super.visitMethod(access, name, desc, signature, exceptions) - - return if (METHOD_NAME == name && METHOD_DESC == desc) { - logger { "FirebaseMessagingServiceClassAdapter: instrumented method $name $desc" } - FirebaseMessagingServiceMethodAdapter(api, nextMethodVisitor) - } else { - nextMethodVisitor - } - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/FirebaseMessagingServiceMethodAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/FirebaseMessagingServiceMethodAdapter.kt deleted file mode 100644 index fde43f804d..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/FirebaseMessagingServiceMethodAdapter.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Visits the onMessageReceived method and inserts a call to - * FirebaseSwazzledHooks._onMessageReceived at the very start of the method. - */ -class FirebaseMessagingServiceMethodAdapter( - api: Int, - methodVisitor: MethodVisitor? -) : MethodVisitor(api, methodVisitor) { - - override fun visitCode() { - // load local variable 'remoteMessage' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 1) - // invoke FirebaseSwazzledHooks._onMessageReceived() - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/fcm/swazzle/callback/com/android/fcm/FirebaseSwazzledHooks", - "_onMessageReceived", - "(Lcom/google/firebase/messaging/RemoteMessage;)V", - false - ) - // call super last to reduce chance of interference with other bytecode instrumentation - super.visitCode() - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InsertSuperOverrideClassVisitor.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InsertSuperOverrideClassVisitor.kt new file mode 100644 index 0000000000..a51d4edd3a --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InsertSuperOverrideClassVisitor.kt @@ -0,0 +1,88 @@ +package io.embrace.android.gradle.plugin.instrumentation.visitor + +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type + +/** + * Visits the class and adds an override that calls to the super class method, if no override is already specified. + */ +class InsertSuperOverrideClassVisitor( + api: Int, + nextClassVisitor: ClassVisitor?, + private val feature: BytecodeInstrumentationFeature, +) : ClassVisitor(api, nextClassVisitor) { + + private var hasOverride = false + + override fun visitMethod( + access: Int, + name: String, + desc: String, + signature: String?, + exceptions: Array?, + ): MethodVisitor? { + val nextMethodVisitor = super.visitMethod(access, name, desc, signature, exceptions) + + return if (feature.targetParams.name == name && feature.targetParams.descriptor == desc && !isStatic(access)) { + hasOverride = true + InstrumentationTargetMethodVisitor( + api = api, + methodVisitor = nextMethodVisitor, + params = feature.insertionParams + ) + } else { + return nextMethodVisitor + } + } + + override fun visitEnd() { + // add an override of onPageStarted if the class does not have one already. + if (!hasOverride) { + val nextMethodVisitor = super.visitMethod( + Opcodes.ACC_PUBLIC, + feature.targetParams.name, + feature.targetParams.descriptor, + null, + emptyArray() + ) + nextMethodVisitor.addSuperCall() + } + super.visitEnd() + } + + private fun MethodVisitor.addSuperCall() { + val count = Type.getType(feature.addOverrideParams?.descriptor).argumentCount + 1 + repeat(count) { + visitVarInsn(Opcodes.ALOAD, it) + } + + visitMethodInsn( + Opcodes.INVOKESPECIAL, + feature.addOverrideParams?.owner, + feature.addOverrideParams?.name, + feature.addOverrideParams?.descriptor, + false + ) + + // load local variables required for method call (if any) and push onto the operand stack + val params = feature.insertionParams + params.operandStackIndices.forEach { + visitVarInsn(Opcodes.ALOAD, it) + } + + // invoke the target method + visitMethodInsn( + Opcodes.INVOKESTATIC, + params.owner, + params.name, + params.descriptor, + false + ) + + visitEnd() + visitInsn(Opcodes.RETURN) + visitMaxs(count, 0) + } +} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InstrumentationTargetClassVisitor.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InstrumentationTargetClassVisitor.kt new file mode 100644 index 0000000000..2c612dcf8c --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InstrumentationTargetClassVisitor.kt @@ -0,0 +1,34 @@ +package io.embrace.android.gradle.plugin.instrumentation.visitor + +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor + +/** + * Visits a class and adds [InstrumentationTargetMethodVisitor] to any methods that require bytecode instrumentation. + */ +class InstrumentationTargetClassVisitor( + api: Int, + nextClassVisitor: ClassVisitor?, + private val feature: BytecodeInstrumentationFeature, +) : ClassVisitor(api, nextClassVisitor) { + + override fun visitMethod( + access: Int, + name: String, + desc: String, + signature: String?, + exceptions: Array?, + ): MethodVisitor? { + val nextMethodVisitor = super.visitMethod(access, name, desc, signature, exceptions) + + return if (feature.targetParams.name == name && feature.targetParams.descriptor == desc && !isStatic(access)) { + InstrumentationTargetMethodVisitor( + api = api, + methodVisitor = nextMethodVisitor, + params = feature.insertionParams + ) + } else { + nextMethodVisitor + } + } +} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InstrumentationTargetMethodVisitor.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InstrumentationTargetMethodVisitor.kt new file mode 100644 index 0000000000..9804350932 --- /dev/null +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/InstrumentationTargetMethodVisitor.kt @@ -0,0 +1,34 @@ +package io.embrace.android.gradle.plugin.instrumentation.visitor + +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes + +/** + * Visits a method that should be rewritten to include an API call to instrumentation + * and inserts a call to a static method at the very start. + */ +internal class InstrumentationTargetMethodVisitor( + api: Int, + methodVisitor: MethodVisitor?, + private val params: BytecodeMethodInsertionParams, +) : MethodVisitor(api, methodVisitor) { + + override fun visitCode() { + // load local variables required for method call (if any) and push onto the operand stack + params.operandStackIndices.forEach { + visitVarInsn(Opcodes.ALOAD, it) + } + + // invoke the target method + visitMethodInsn( + Opcodes.INVOKESTATIC, + params.owner, + params.name, + params.descriptor, + false + ) + + // call super last to reduce chance of interference with other bytecode instrumentation + super.visitCode() + } +} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpClassAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpClassAdapter.kt deleted file mode 100644 index 4aac92acce..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpClassAdapter.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import com.android.build.api.instrumentation.ClassContext -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.MethodVisitor - -/** - * Visits a class and returns an [OkHttpInitMethodAdapter] for OkHttp$Builder methods. - */ -class OkHttpClassAdapter( - api: Int, - internal val nextClassVisitor: ClassVisitor?, - private val logger: (() -> String) -> Unit -) : ClassVisitor(api, nextClassVisitor) { - - companion object : ClassVisitFilter { - private const val CLASS_NAME = "okhttp3.OkHttpClient\$Builder" - private const val METHOD_NAME_BUILD = "build" - private const val METHOD_DESC_BUILD = "()Lokhttp3/OkHttpClient;" - - override fun accept(classContext: ClassContext): Boolean { - return classContext.currentClassData.className == CLASS_NAME - } - } - - override fun visitMethod( - access: Int, - name: String, - desc: String, - signature: String?, - exceptions: Array? - ): MethodVisitor? { - val nextMethodVisitor = super.visitMethod(access, name, desc, signature, exceptions) - - return if (METHOD_NAME_BUILD == name && METHOD_DESC_BUILD == desc) { - logger { "OkHttpClassAdapter: instrumented method $name $desc" } - OkHttpMethodAdapter(api, nextMethodVisitor) - } else { - nextMethodVisitor - } - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpMethodAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpMethodAdapter.kt deleted file mode 100644 index 698468d1b5..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpMethodAdapter.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Visits OkHttp methods and inserts a call to OkHttpClient$Builder._preBuild at the very start - * of the method. - */ -class OkHttpMethodAdapter( - api: Int, - methodVisitor: MethodVisitor? -) : MethodVisitor(api, methodVisitor) { - - override fun visitCode() { - // load local variable 'this' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 0) - - // invoke Builder._preBuild(this) - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient\$Builder", - "_preBuild", - "(Lokhttp3/OkHttpClient\$Builder;)V", - false - ) - - // call super last to reduce chance of interference with other bytecode instrumentation - super.visitCode() - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickClassAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickClassAdapter.kt deleted file mode 100644 index 1b4ac8dec6..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickClassAdapter.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import com.android.build.api.instrumentation.ClassContext -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.MethodVisitor - -/** - * Visits a class and returns an [OnClickMethodAdapter] for any onClick method which - * conforms to the OnClickListener interface. - */ -class OnClickClassAdapter( - api: Int, - internal val nextClassVisitor: ClassVisitor?, - private val logger: (() -> String) -> Unit -) : ClassVisitor(api, nextClassVisitor) { - - companion object : ClassVisitFilter { - private const val METHOD_NAME = "onClick" - private const val METHOD_DESC = "(Landroid/view/View;)V" - - override fun accept(classContext: ClassContext) = true - } - - override fun visitMethod( - access: Int, - name: String, - desc: String, - signature: String?, - exceptions: Array? - ): MethodVisitor? { - val nextMethodVisitor = super.visitMethod(access, name, desc, signature, exceptions) - - return if (METHOD_NAME == name && METHOD_DESC == desc && !isStatic(access)) { - logger { "OnClickClassAdapter: instrumented method $name $desc" } - OnClickMethodAdapter(api, nextMethodVisitor) - } else if (METHOD_DESC == desc && isStatic(access) && isSynthetic(access)) { - logger { "OnClickClassAdapter: instrumented synthetic method $name $desc" } - OnClickStaticMethodAdapter(api, nextMethodVisitor) - } else { - nextMethodVisitor - } - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickMethodAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickMethodAdapter.kt deleted file mode 100644 index 63d220c91d..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickMethodAdapter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Visits an onClick method and inserts a call to ViewSwazzledHooks._preOnClick at the very start - * of the method. - */ -class OnClickMethodAdapter( - api: Int, - methodVisitor: MethodVisitor? -) : MethodVisitor(api, methodVisitor) { - - override fun visitCode() { - // load local variable 'this' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 0) - - // load local variable 'view' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 1) - - // invoke ViewSwazzledHooks$OnClickListener._preOnClick() - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/ViewSwazzledHooks\$OnClickListener", - "_preOnClick", - "(Landroid/view/View\$OnClickListener;Landroid/view/View;)V", - false - ) - - // call super last to reduce chance of interference with other bytecode instrumentation - super.visitCode() - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickStaticMethodAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickStaticMethodAdapter.kt deleted file mode 100644 index f98f94480f..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickStaticMethodAdapter.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Visits a static onClick method and inserts a call to ViewSwazzledHooks._preOnClick at the very - * start of the method. - */ -class OnClickStaticMethodAdapter( - api: Int, - methodVisitor: MethodVisitor?, -) : MethodVisitor(api, methodVisitor) { - - override fun visitCode() { - // load the null reference and push it onto the operand stack. - // null is ok here as _preOnClick doesn't use the listener, - // and in a static context 'this' does not actually implement OnClickListener - // in Java bytecode. - visitInsn(Opcodes.ACONST_NULL) - - // load local variable 'view' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 0) - - // invoke ViewSwazzledHooks$OnClickListener._preOnClick() - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/ViewSwazzledHooks\$OnClickListener", - "_preOnClick", - "(Landroid/view/View\$OnClickListener;Landroid/view/View;)V", - false - ) - - // call super last to reduce chance of interference with other bytecode instrumentation - super.visitCode() - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickClassAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickClassAdapter.kt deleted file mode 100644 index 69a494e9c7..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickClassAdapter.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import com.android.build.api.instrumentation.ClassContext -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.MethodVisitor - -/** - * Visits a class and returns an [OnLongClickMethodAdapter] for any onLongClick method which - * conforms to the OnLongClickListener interface. - */ -class OnLongClickClassAdapter( - api: Int, - internal val nextClassVisitor: ClassVisitor?, - private val logger: (() -> String) -> Unit -) : ClassVisitor(api, nextClassVisitor) { - - companion object : ClassVisitFilter { - private const val METHOD_NAME = "onLongClick" - private const val METHOD_DESC = "(Landroid/view/View;)Z" - - override fun accept(classContext: ClassContext) = true - } - - override fun visitMethod( - access: Int, - name: String, - desc: String, - signature: String?, - exceptions: Array? - ): MethodVisitor? { - val nextMethodVisitor = super.visitMethod(access, name, desc, signature, exceptions) - - return if (METHOD_NAME == name && METHOD_DESC == desc && !isStatic(access)) { - logger { "OnLongClickClassAdapter: instrumented method $name $desc" } - OnLongClickMethodAdapter(api, nextMethodVisitor) - } else if (METHOD_DESC == desc && isStatic(access) && isSynthetic(access)) { - logger { "OnLongClickClassAdapter: instrumented synthetic method $name $desc" } - OnLongClickStaticMethodAdapter(api, nextMethodVisitor) - } else { - nextMethodVisitor - } - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickMethodAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickMethodAdapter.kt deleted file mode 100644 index 48197e79cb..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickMethodAdapter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Visits an onLongClick method and inserts a call to ViewSwazzledHooks._preOnLongClick at - * the very start of the method. - */ -class OnLongClickMethodAdapter( - api: Int, - methodVisitor: MethodVisitor? -) : MethodVisitor(api, methodVisitor) { - - override fun visitCode() { - // load local variable 'this' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 0) - - // load local variable 'view' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 1) - - // invoke ViewSwazzledHooks$OnLongClickListener._preOnLongClick() - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/ViewSwazzledHooks\$OnLongClickListener", - "_preOnLongClick", - "(Landroid/view/View\$OnLongClickListener;Landroid/view/View;)V", - false - ) - - // call super last to reduce chance of interference with other bytecode instrumentation - super.visitCode() - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickStaticMethodAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickStaticMethodAdapter.kt deleted file mode 100644 index dded713ade..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickStaticMethodAdapter.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Visits a static onLongClick method and inserts a call to ViewSwazzledHooks._preOnLongClick - * at the very start of the method. - */ -class OnLongClickStaticMethodAdapter( - api: Int, - methodVisitor: MethodVisitor?, -) : MethodVisitor(api, methodVisitor) { - - override fun visitCode() { - // load the null reference and push it onto the operand stack. - // null is ok here as _preOnLongClick doesn't use the listener, - // and in a static context 'this' does not actually implement OnLongClickListener - // in Java bytecode. - visitInsn(Opcodes.ACONST_NULL) - - // load local variable 'view' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 0) - - // invoke ViewSwazzledHooks$OnLongClickListener._preOnLongClick() - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/ViewSwazzledHooks\$OnLongClickListener", - "_preOnLongClick", - "(Landroid/view/View\$OnLongClickListener;Landroid/view/View;)V", - false - ) - - // call super last to reduce chance of interference with other bytecode instrumentation - super.visitCode() - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OpcodeExtensions.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OpcodeExtensions.kt index eb69dc3ce6..8044ee85f2 100644 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OpcodeExtensions.kt +++ b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OpcodeExtensions.kt @@ -6,8 +6,3 @@ import org.objectweb.asm.Opcodes * Returns true if a method is static. */ internal fun isStatic(access: Int) = access.and(Opcodes.ACC_STATIC) != 0 - -/** - * Returns true if a method is synthetic (e.g. a Java lambda). - */ -internal fun isSynthetic(access: Int) = access.and(Opcodes.ACC_SYNTHETIC) != 0 diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientClassAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientClassAdapter.kt deleted file mode 100644 index 1ed5cbab67..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientClassAdapter.kt +++ /dev/null @@ -1,68 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import com.android.build.api.instrumentation.ClassContext -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Visits the [WebViewClient] class and returns a [WebViewClientMethodAdapter] for the - * onPageStarted method. - */ -class WebViewClientClassAdapter( - api: Int, - internal val nextClassVisitor: ClassVisitor?, - private val logger: (() -> String) -> Unit -) : ClassVisitor(api, nextClassVisitor) { - - companion object : ClassVisitFilter { - private const val CLASS_NAME = "android.webkit.WebViewClient" - private const val METHOD_NAME = "onPageStarted" - private const val METHOD_DESC = - "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V" - - override fun accept(classContext: ClassContext): Boolean { - if (classContext.currentClassData.superClasses.contains(CLASS_NAME)) { - return true - } - return false - } - } - - var hasOverride = false - - override fun visitMethod( - access: Int, - name: String, - desc: String, - signature: String?, - exceptions: Array? - ): MethodVisitor? { - val nextMethodVisitor = super.visitMethod(access, name, desc, signature, exceptions) - - return if (METHOD_NAME == name && METHOD_DESC == desc) { - logger { "WebViewClientClassAdapter: instrumented method $name $desc" } - hasOverride = true - WebViewClientMethodAdapter(api, nextMethodVisitor) - } else { - nextMethodVisitor - } - } - - override fun visitEnd() { - // add an override of onPageStarted if the class does not have one already. - if (!hasOverride) { - logger { "WebViewClientClassAdapter: instrumented method $METHOD_NAME $METHOD_DESC (added override)" } - - val nextMethodVisitor = super.visitMethod( - Opcodes.ACC_PUBLIC, - METHOD_NAME, - METHOD_DESC, - null, - emptyArray() - ) - WebViewClientOverrideMethodAdapter(api, nextMethodVisitor).visitEnd() - } - super.visitEnd() - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientMethodAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientMethodAdapter.kt deleted file mode 100644 index 6bc13b6ec2..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientMethodAdapter.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Visits the onPageStarted method and inserts a call to ViewSwazzledHooks._preOnPageStarted - * at the very start of the method. - */ -open class WebViewClientMethodAdapter( - api: Int, - methodVisitor: MethodVisitor? -) : MethodVisitor(api, methodVisitor) { - - override fun visitCode() { - instrumentOnPageStarted() - // call super last to reduce chance of interference with other bytecode instrumentation - super.visitCode() - } - - internal fun instrumentOnPageStarted() { - // load local variable 'view' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 1) - - // load local variable 'url' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 2) - - // load local variable 'favicon' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 3) - - // invoke WebViewClientSwazzledHooks._preOnPageStarted() - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/WebViewClientSwazzledHooks", - "_preOnPageStarted", - "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V", - false - ) - } -} diff --git a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientOverrideMethodAdapter.kt b/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientOverrideMethodAdapter.kt deleted file mode 100644 index be929c63fe..0000000000 --- a/embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientOverrideMethodAdapter.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -/** - * Creates an onPageStarted method override and inserts a call to - * ViewSwazzledHooks._preOnPageStarted at the very start of the method. - */ -class WebViewClientOverrideMethodAdapter( - api: Int, - methodVisitor: MethodVisitor? -) : WebViewClientMethodAdapter(api, methodVisitor) { - - override fun visitEnd() { - instrumentOnPageStarted() - addSuperCall() - super.visitEnd() - visitInsn(Opcodes.RETURN) - visitMaxs(4, 0) - } - - private fun addSuperCall() { - // load local variable 'this' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 0) - - // load local variable 'view' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 1) - - // load local variable 'url' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 2) - - // load local variable 'favicon' and push it onto the operand stack - visitVarInsn(Opcodes.ALOAD, 3) - - visitMethodInsn( - Opcodes.INVOKESPECIAL, - "android/webkit/WebViewClient", - "onPageStarted", - "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V", - false - ) - } -} diff --git a/embrace-gradle-plugin/src/main/resources/bytecode_instrumentation_features.json b/embrace-gradle-plugin/src/main/resources/bytecode_instrumentation_features.json new file mode 100644 index 0000000000..3c1db11342 --- /dev/null +++ b/embrace-gradle-plugin/src/main/resources/bytecode_instrumentation_features.json @@ -0,0 +1,102 @@ +{ + "features": [ + { + "name": "fcm_push_notifications", + "target": { + "name": "onMessageReceived", + "descriptor": "(Lcom/google/firebase/messaging/RemoteMessage;)V" + }, + "insert": { + "owner": "io/embrace/android/embracesdk/internal/instrumentation/bytecode/FcmBytecodeEntrypoint", + "name": "onMessageReceived", + "descriptor": "(Lcom/google/firebase/messaging/RemoteMessage;)V", + "operandStackIndices": [ + 1 + ] + }, + "visitStrategy": { + "type": "match_super_class_name", + "value": "com.google.firebase.messaging.FirebaseMessagingService" + } + }, + { + "name": "webview_page_start", + "target": { + "name": "onPageStarted", + "descriptor": "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V" + }, + "insert": { + "owner": "io/embrace/android/embracesdk/internal/instrumentation/bytecode/WebViewClientBytecodeEntrypoint", + "name": "onPageStarted", + "descriptor": "(Ljava/lang/String;)V", + "operandStackIndices": [ + 2 + ] + }, + "addOverride": { + "owner": "android/webkit/WebViewClient", + "name": "onPageStarted", + "descriptor": "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V" + }, + "visitStrategy": { + "type": "match_super_class_name", + "value": "android.webkit.WebViewClient" + } + }, + { + "name": "okhttp", + "target": { + "name": "build", + "descriptor": "()Lokhttp3/OkHttpClient;" + }, + "insert": { + "owner": "io/embrace/android/embracesdk/internal/instrumentation/bytecode/OkHttpBytecodeEntrypoint", + "name": "build", + "descriptor": "(Lokhttp3/OkHttpClient$Builder;)V", + "operandStackIndices": [ + 0 + ] + }, + "visitStrategy": { + "type": "match_class_name", + "value": "okhttp3.OkHttpClient$Builder" + } + }, + { + "name": "on_click", + "target": { + "name": "onClick", + "descriptor": "(Landroid/view/View;)V" + }, + "insert": { + "owner": "io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnClickBytecodeEntrypoint", + "name": "onClick", + "descriptor": "(Landroid/view/View;)V", + "operandStackIndices": [ + 1 + ] + }, + "visitStrategy": { + "type": "exhaustive" + } + }, + { + "name": "on_long_click", + "target": { + "name": "onLongClick", + "descriptor": "(Landroid/view/View;)Z" + }, + "insert": { + "owner": "io/embrace/android/embracesdk/internal/instrumentation/bytecode/OnLongClickBytecodeEntrypoint", + "name": "onLongClick", + "descriptor": "(Landroid/view/View;)V", + "operandStackIndices": [ + 1 + ] + }, + "visitStrategy": { + "type": "exhaustive" + } + } + ] +} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/EmbraceClassVisitorFactoryTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/EmbraceClassVisitorFactoryTest.kt index 42de077e4f..fd305b9ee7 100644 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/EmbraceClassVisitorFactoryTest.kt +++ b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/EmbraceClassVisitorFactoryTest.kt @@ -1,17 +1,10 @@ package io.embrace.android.gradle.plugin.instrumentation import com.android.build.api.instrumentation.ClassContext -import io.embrace.android.gradle.plugin.instrumentation.config.visitor.ConfigInstrumentationClassVisitor import io.embrace.android.gradle.plugin.instrumentation.fakes.TestBytecodeInstrumentationParams -import io.embrace.android.gradle.plugin.instrumentation.fakes.TestClassContext import io.embrace.android.gradle.plugin.instrumentation.fakes.TestClassData import io.embrace.android.gradle.plugin.instrumentation.fakes.TestClassVisitor import io.embrace.android.gradle.plugin.instrumentation.fakes.TestVisitorFactoryImpl -import io.embrace.android.gradle.plugin.instrumentation.visitor.FirebaseMessagingServiceClassAdapter -import io.embrace.android.gradle.plugin.instrumentation.visitor.OkHttpClassAdapter -import io.embrace.android.gradle.plugin.instrumentation.visitor.OnClickClassAdapter -import io.embrace.android.gradle.plugin.instrumentation.visitor.OnLongClickClassAdapter -import io.embrace.android.gradle.plugin.instrumentation.visitor.WebViewClientClassAdapter import io.mockk.every import io.mockk.mockk import org.junit.Assert.assertFalse @@ -24,67 +17,6 @@ class EmbraceClassVisitorFactoryTest { private val clzDataString = TestClassData(checkNotNull(String::class.qualifiedName)) private val clzDataBool = TestClassData(checkNotNull(Boolean::class.qualifiedName)) - /** - * Verifies that the OnClick visitor should be returned if there is nothing to instrument, - * and that it should chain the original visitor. - */ - @Test - fun testDefaultClassVisitorReturned() { - val visitor = TestClassVisitor() - val ctx = TestClassContext(clzDataString) - val returningVisitor = TestVisitorFactoryImpl().createClassVisitor(ctx, visitor) - check(returningVisitor is OnClickClassAdapter) - check(returningVisitor.nextClassVisitor is OnLongClickClassAdapter) - assertSame(visitor, returningVisitor.nextClassVisitor.nextClassVisitor) - } - - @Test - fun testWebViewClassVisitorReturned() { - val visitor = TestClassVisitor() - val ctx = createMockClassContext("android.webkit.WebViewClient") - val returningVisitor = TestVisitorFactoryImpl().createClassVisitor(ctx, visitor) - check(returningVisitor is OnClickClassAdapter) - check(returningVisitor.nextClassVisitor is OnLongClickClassAdapter) - check(returningVisitor.nextClassVisitor.nextClassVisitor is WebViewClientClassAdapter) - assertSame(visitor, returningVisitor.nextClassVisitor.nextClassVisitor.nextClassVisitor) - } - - @Test - fun testConfigClassVisitorReturned() { - val visitor = TestClassVisitor() - val ctx = - createMockClassContext( - "io.embrace.android.embracesdk.internal.config.instrumented.EnabledFeatureConfigImpl" - ) - val returningVisitor = TestVisitorFactoryImpl().createClassVisitor(ctx, visitor) - check(returningVisitor is OnClickClassAdapter) - check(returningVisitor.nextClassVisitor is OnLongClickClassAdapter) - check(returningVisitor.nextClassVisitor.nextClassVisitor is ConfigInstrumentationClassVisitor) - } - - @Test - fun testOkHttpClassVisitorReturned() { - val visitor = TestClassVisitor() - val ctx = createMockClassContext("okhttp3.OkHttpClient\$Builder") - val returningVisitor = TestVisitorFactoryImpl().createClassVisitor(ctx, visitor) - check(returningVisitor is OnClickClassAdapter) - check(returningVisitor.nextClassVisitor is OnLongClickClassAdapter) - check(returningVisitor.nextClassVisitor.nextClassVisitor is OkHttpClassAdapter) - assertSame(visitor, returningVisitor.nextClassVisitor.nextClassVisitor.nextClassVisitor) - } - - @Test - fun testFCMClassVisitorReturned() { - val visitor = TestClassVisitor() - val ctx = createMockClassContext("com.google.firebase.messaging.FirebaseMessagingService") - val params = TestBytecodeInstrumentationParams(instrumentFirebaseMessaging = true) - val returningVisitor = TestVisitorFactoryImpl(params = params).createClassVisitor(ctx, visitor) - check(returningVisitor is OnClickClassAdapter) - check(returningVisitor.nextClassVisitor is OnLongClickClassAdapter) - check(returningVisitor.nextClassVisitor.nextClassVisitor is FirebaseMessagingServiceClassAdapter) - assertSame(visitor, returningVisitor.nextClassVisitor.nextClassVisitor.nextClassVisitor) - } - @Test fun testAsmApiEnabled() { val factory = TestVisitorFactoryImpl(params = TestBytecodeInstrumentationParams(disabled = false)) @@ -111,25 +43,6 @@ class EmbraceClassVisitorFactoryTest { assertFalse(factory.isInstrumentable(clzDataBool)) } - @Test - fun testOnClickVisitorDisabled() { - val visitor = TestClassVisitor() - val ctx = TestClassContext(clzDataString) - val config = createInstrumentationConfig(instrumentOnClick = false) - val observed = fetchClassVisitor(config, ctx, visitor) - assertTrue(observed is OnLongClickClassAdapter) - } - - @Test - fun testOnLongClickVisitorDisabled() { - val visitor = TestClassVisitor() - val ctx = TestClassContext(clzDataString) - val config = createInstrumentationConfig(instrumentOnLongClick = false) - val observed = fetchClassVisitor(config, ctx, visitor) - check(observed is OnClickClassAdapter) - check(observed.nextClassVisitor is TestClassVisitor) - } - @Test fun testWebViewClassVisitorDisabled() { val visitor = TestClassVisitor() diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/config/arch/InstrumentedConfigClassTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/config/arch/InstrumentedConfigClassTest.kt index ee782dc179..1f9ef7b26d 100644 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/config/arch/InstrumentedConfigClassTest.kt +++ b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/config/arch/InstrumentedConfigClassTest.kt @@ -5,7 +5,6 @@ import io.embrace.android.gradle.plugin.instrumentation.config.BooleanReturnValu import io.embrace.android.gradle.plugin.instrumentation.config.IntReturnValueMethodVisitor import io.embrace.android.gradle.plugin.instrumentation.config.LongReturnValueMethodVisitor import io.embrace.android.gradle.plugin.instrumentation.config.MapReturnValueMethodVisitor -import io.embrace.android.gradle.plugin.instrumentation.config.StringListReturnValueMethodVisitor import io.embrace.android.gradle.plugin.instrumentation.config.StringReturnValueMethodVisitor import io.mockk.mockk import org.junit.Assert.assertEquals diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpClassAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpClassAdapterTest.kt deleted file mode 100644 index 2dca27dd4e..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpClassAdapterTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.embrace.android.gradle.plugin.instrumentation.ASM_API_VERSION -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test -import org.objectweb.asm.Opcodes - -class OkHttpClassAdapterTest { - - private val adapter = OkHttpClassAdapter(ASM_API_VERSION, null) {} - - @Test - fun testCtorVisited() { - val visitor = adapter.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, emptyArray()) - assertFalse(visitor is OkHttpMethodAdapter) - } - - @Test - fun testBuildVisited() { - val visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "build", - "()Lokhttp3/OkHttpClient;", - null, - emptyArray() - ) - assertTrue(visitor is OkHttpMethodAdapter) - } - - @Test - fun testMethodNotVisited() { - var visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "foo", - "()Lokhttp3/OkHttpClient;", - null, - emptyArray() - ) - assertFalse(visitor is OkHttpMethodAdapter) - - visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "build", - "()Lokhttp3/OkHttpClient\$Builder;", - null, - emptyArray() - ) - assertFalse(visitor is OkHttpMethodAdapter) - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpConfigMethodAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpConfigMethodAdapterTest.kt deleted file mode 100644 index 1476a14a59..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OkHttpConfigMethodAdapterTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -class OkHttpConfigMethodAdapterTest { - - @Test - fun visitCode() { - val visitor = mockk(relaxed = true) - OkHttpMethodAdapter(Opcodes.ASM6, visitor).visitCode() - verify(exactly = 1) { - with(visitor) { - visitVarInsn(Opcodes.ALOAD, 0) - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/okhttp3/swazzle/callback/okhttp3/OkHttpClient\$Builder", - "_preBuild", - "(Lokhttp3/OkHttpClient\$Builder;)V", - false - ) - visitCode() - } - } - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickClassAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickClassAdapterTest.kt deleted file mode 100644 index 0104a2b9e6..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickClassAdapterTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.embrace.android.gradle.plugin.instrumentation.ASM_API_VERSION -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test -import org.objectweb.asm.Opcodes - -class OnClickClassAdapterTest { - - private val adapter = OnClickClassAdapter(ASM_API_VERSION, null) {} - - @Test - fun testOnClickVisited() { - val visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "onClick", - "(Landroid/view/View;)V", - null, - emptyArray() - ) - assertTrue(visitor is OnClickMethodAdapter) - } - - @Test - fun testStaticOnClickVisited() { - val access = Opcodes.ACC_STATIC.plus(Opcodes.ACC_SYNTHETIC) - val visitor = - adapter.visitMethod(access, "onClick", "(Landroid/view/View;)V", null, emptyArray()) - assertTrue(visitor is OnClickStaticMethodAdapter) - } - - @Test - fun testMethodNotVisited() { - var visitor = adapter.visitMethod(Opcodes.ACC_PUBLIC, "onClick", "()V", null, emptyArray()) - assertFalse(visitor is OnClickMethodAdapter) - - visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "foo", - "(Landroid/view/View;)V", - null, - emptyArray() - ) - assertFalse(visitor is OnClickMethodAdapter) - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickConfigMethodAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickConfigMethodAdapterTest.kt deleted file mode 100644 index 9dd6a7608e..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickConfigMethodAdapterTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -class OnClickConfigMethodAdapterTest { - - @Test - fun visitCode() { - val visitor = mockk(relaxed = true) - OnClickMethodAdapter(Opcodes.ASM6, visitor).visitCode() - verify(exactly = 1) { - with(visitor) { - visitVarInsn(Opcodes.ALOAD, 0) - visitVarInsn(Opcodes.ALOAD, 1) - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/ViewSwazzledHooks\$OnClickListener", - "_preOnClick", - "(Landroid/view/View\$OnClickListener;Landroid/view/View;)V", - false - ) - visitCode() - } - } - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickStaticConfigMethodAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickStaticConfigMethodAdapterTest.kt deleted file mode 100644 index 35163cec65..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnClickStaticConfigMethodAdapterTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -class OnClickStaticConfigMethodAdapterTest { - - @Test - fun visitCode() { - val visitor = mockk(relaxed = true) - OnClickStaticMethodAdapter(Opcodes.ASM6, visitor).visitCode() - verify(exactly = 1) { - with(visitor) { - visitInsn(Opcodes.ACONST_NULL) - visitVarInsn(Opcodes.ALOAD, 0) - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/ViewSwazzledHooks\$OnClickListener", - "_preOnClick", - "(Landroid/view/View\$OnClickListener;Landroid/view/View;)V", - false - ) - visitCode() - } - } - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickClassAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickClassAdapterTest.kt deleted file mode 100644 index e77d961808..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickClassAdapterTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.embrace.android.gradle.plugin.instrumentation.ASM_API_VERSION -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test -import org.objectweb.asm.Opcodes - -class OnLongClickClassAdapterTest { - - private val adapter = OnLongClickClassAdapter(ASM_API_VERSION, null) {} - - @Test - fun testOnLongClickVisited() { - val visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "onLongClick", - "(Landroid/view/View;)Z", - null, - emptyArray() - ) - assertTrue(visitor is OnLongClickMethodAdapter) - } - - @Test - fun testStaticOnLongClickVisited() { - val access = Opcodes.ACC_STATIC.plus(Opcodes.ACC_SYNTHETIC) - val visitor = - adapter.visitMethod(access, "onLongClick", "(Landroid/view/View;)Z", null, emptyArray()) - assertTrue(visitor is OnLongClickStaticMethodAdapter) - } - - @Test - fun testMethodNotVisited() { - var visitor = - adapter.visitMethod(Opcodes.ACC_PUBLIC, "onLongClick", "()V", null, emptyArray()) - assertFalse(visitor is OnLongClickMethodAdapter) - - visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "foo", - "(Landroid/view/View;)Z", - null, - emptyArray() - ) - assertFalse(visitor is OnLongClickMethodAdapter) - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickConfigMethodAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickConfigMethodAdapterTest.kt deleted file mode 100644 index 1392360173..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickConfigMethodAdapterTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -class OnLongClickConfigMethodAdapterTest { - - @Test - fun visitCode() { - val visitor = mockk(relaxed = true) - OnLongClickMethodAdapter(Opcodes.ASM6, visitor).visitCode() - verify(exactly = 1) { - with(visitor) { - visitVarInsn(Opcodes.ALOAD, 0) - visitVarInsn(Opcodes.ALOAD, 1) - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/ViewSwazzledHooks\$OnLongClickListener", - "_preOnLongClick", - "(Landroid/view/View\$OnLongClickListener;Landroid/view/View;)V", - false - ) - visitCode() - } - } - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickStaticConfigMethodAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickStaticConfigMethodAdapterTest.kt deleted file mode 100644 index 75d814367c..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/OnLongClickStaticConfigMethodAdapterTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -class OnLongClickStaticConfigMethodAdapterTest { - - @Test - fun visitCode() { - val visitor = mockk(relaxed = true) - OnLongClickStaticMethodAdapter(Opcodes.ASM6, visitor).visitCode() - verify(exactly = 1) { - with(visitor) { - visitInsn(Opcodes.ACONST_NULL) - visitVarInsn(Opcodes.ALOAD, 0) - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/ViewSwazzledHooks\$OnLongClickListener", - "_preOnLongClick", - "(Landroid/view/View\$OnLongClickListener;Landroid/view/View;)V", - false - ) - visitCode() - } - } - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientClassAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientClassAdapterTest.kt deleted file mode 100644 index 4d4a2a6996..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientClassAdapterTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.embrace.android.gradle.plugin.instrumentation.ASM_API_VERSION -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test -import org.objectweb.asm.Opcodes - -class WebViewClientClassAdapterTest { - - private val adapter = WebViewClientClassAdapter(ASM_API_VERSION, null) {} - - @Test - fun testOnPageStartedVisited() { - val visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "onPageStarted", - "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V", - null, - emptyArray() - ) - assertTrue(visitor is WebViewClientMethodAdapter) - } - - @Test - fun testMethodNotVisited() { - var visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "onPageStarted", - "(Landroid/webkit/WebView;Ljava/lang/Boolean;Landroid/graphics/Bitmap;)V", - null, - emptyArray() - ) - assertFalse(visitor is WebViewClientMethodAdapter) - - visitor = adapter.visitMethod( - Opcodes.ACC_PUBLIC, - "onPageStart", - "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V", - null, - emptyArray() - ) - assertFalse(visitor is WebViewClientMethodAdapter) - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientMethodAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientMethodAdapterTest.kt deleted file mode 100644 index c532d0ddc8..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientMethodAdapterTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -class WebViewClientMethodAdapterTest { - - @Test - fun visitCode() { - val visitor = mockk(relaxed = true) - WebViewClientMethodAdapter(Opcodes.ASM6, visitor).visitCode() - verify(exactly = 1) { - with(visitor) { - visitVarInsn(Opcodes.ALOAD, 1) - visitVarInsn(Opcodes.ALOAD, 2) - visitVarInsn(Opcodes.ALOAD, 3) - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/WebViewClientSwazzledHooks", - "_preOnPageStarted", - "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V", - false - ) - } - } - } -} diff --git a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientOverrideConfigMethodAdapterTest.kt b/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientOverrideConfigMethodAdapterTest.kt deleted file mode 100644 index 78abae800c..0000000000 --- a/embrace-gradle-plugin/src/test/java/io/embrace/android/gradle/plugin/instrumentation/visitor/WebViewClientOverrideConfigMethodAdapterTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.embrace.android.gradle.plugin.instrumentation.visitor - -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -class WebViewClientOverrideConfigMethodAdapterTest { - - @Test - fun visitCode() { - val visitor = mockk(relaxed = true) - WebViewClientOverrideMethodAdapter(Opcodes.ASM6, visitor).visitCode() - verify(exactly = 1) { - with(visitor) { - visitVarInsn(Opcodes.ALOAD, 1) - visitVarInsn(Opcodes.ALOAD, 2) - visitVarInsn(Opcodes.ALOAD, 3) - visitMethodInsn( - Opcodes.INVOKESTATIC, - "io/embrace/android/embracesdk/WebViewClientSwazzledHooks", - "_preOnPageStarted", - "(Landroid/webkit/WebView;Ljava/lang/String;Landroid/graphics/Bitmap;)V", - false - ) - } - } - } -} diff --git a/embrace-test-fakes/lint-baseline.xml b/embrace-test-fakes/lint-baseline.xml index 333f47a062..f4b541243b 100644 --- a/embrace-test-fakes/lint-baseline.xml +++ b/embrace-test-fakes/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -19,7 +19,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -30,7 +30,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -41,7 +41,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -74,7 +74,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -85,106 +85,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -232,83 +133,6 @@ column="9"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/CodeExample.kt b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/CodeExample.kt index 3ebbcead1d..5ff0cbf7a4 100644 --- a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/CodeExample.kt +++ b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/CodeExample.kt @@ -15,7 +15,8 @@ enum class CodeExample( END_SESSION("End Session"), ALTER_USER("Alter User"), VIEW_TRACKING("View Tracking"), - SDK_STATE_API("SDK State API"); + SDK_STATE_API("SDK State API"), + BYTECODE_INSTRUMENTATION_SAMPLES("Bytecode Instrumentation"); val route: String = name.lowercase() } diff --git a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/ExampleContent.kt b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/ExampleContent.kt index 6e87f6bbfe..d53cd1f3b8 100644 --- a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/ExampleContent.kt +++ b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/ExampleContent.kt @@ -3,6 +3,7 @@ package io.embrace.android.exampleapp.ui import androidx.compose.runtime.Composable import io.embrace.android.exampleapp.ui.examples.AddBreadcrumbExample import io.embrace.android.exampleapp.ui.examples.AnrDetectionExample +import io.embrace.android.exampleapp.ui.examples.bytecode.BytecodeInstrumentationExample import io.embrace.android.exampleapp.ui.examples.EndSessionExample import io.embrace.android.exampleapp.ui.examples.JvmCrashExample import io.embrace.android.exampleapp.ui.examples.LogMessageAttachmentsExample @@ -31,5 +32,6 @@ fun ExampleContent(example: CodeExample) { CodeExample.JVM_CRASH -> JvmCrashExample() CodeExample.NDK_CRASH -> NdkCrashExample() CodeExample.ANR_DETECTION -> AnrDetectionExample() + CodeExample.BYTECODE_INSTRUMENTATION_SAMPLES -> BytecodeInstrumentationExample() } } diff --git a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeFcmPushNotificationActivity.kt b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeFcmPushNotificationActivity.kt new file mode 100644 index 0000000000..d678ffcc3c --- /dev/null +++ b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeFcmPushNotificationActivity.kt @@ -0,0 +1,24 @@ +package io.embrace.android.exampleapp.ui.examples.bytecode + +import android.os.Bundle +import android.view.View +import androidx.activity.ComponentActivity +import com.google.firebase.messaging.RemoteMessage +import io.embrace.android.exampleapp.R + +class BytecodeFcmPushNotificationActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_bytecode_fcm_push_notification) + + findViewById(R.id.btn_bytecode_fcm_push_notification).setOnClickListener { + val msg = RemoteMessage.Builder("my-id") + .setMessageId("my-message-id") + .setMessageType("my-message-type") + .setData(mapOf("key" to "value")) + .build() + ExamplePushNotificationService().onMessageReceived(msg) + } + } +} diff --git a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeInstrumentationExample.kt b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeInstrumentationExample.kt new file mode 100644 index 0000000000..12f9315b68 --- /dev/null +++ b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeInstrumentationExample.kt @@ -0,0 +1,33 @@ +package io.embrace.android.exampleapp.ui.examples.bytecode + +import android.content.Intent +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import kotlin.reflect.KClass + +private data class BytecodeSample( + val name: String, + val clz: KClass<*>, +) + +private val items = listOf( + BytecodeSample("OnClick/OnLongClick", BytecodeViewClickActivity::class), + BytecodeSample("WebView", BytecodeWebViewActivity::class), + BytecodeSample("OkHttp", BytecodeOkHttpActivity::class), + BytecodeSample("FCM Push Notifications", BytecodeFcmPushNotificationActivity::class), +) + +@Composable +fun BytecodeInstrumentationExample() { + val ctx = androidx.compose.ui.platform.LocalContext.current + + items.forEach { + Button(onClick = { + val intent = Intent(ctx, it.clz.java) + ctx.startActivity(intent) + }) { + Text(it.name) + } + } +} diff --git a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeOkHttpActivity.kt b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeOkHttpActivity.kt new file mode 100644 index 0000000000..016867e20e --- /dev/null +++ b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeOkHttpActivity.kt @@ -0,0 +1,31 @@ +package io.embrace.android.exampleapp.ui.examples.bytecode + +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.activity.ComponentActivity +import io.embrace.android.exampleapp.R +import okhttp3.OkHttpClient +import okhttp3.Request + +class BytecodeOkHttpActivity : ComponentActivity() { + + private val client = OkHttpClient.Builder().build() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_bytecode_okhttp) + + findViewById(R.id.btn_bytecode_okhttp).setOnClickListener { + client.newCall(Request.Builder().url("https://embrace.io/").build()).enqueue(object : okhttp3.Callback { + override fun onFailure(call: okhttp3.Call, e: java.io.IOException) { + Log.i("EmbraceTestApp", "Network request failed ${e.message}") + } + + override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) { + Log.i("EmbraceTestApp", "Network request completed ${response.code}") + } + }) + } + } +} diff --git a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeViewClickActivity.kt b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeViewClickActivity.kt new file mode 100644 index 0000000000..613ee86bef --- /dev/null +++ b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeViewClickActivity.kt @@ -0,0 +1,27 @@ +package io.embrace.android.exampleapp.ui.examples.bytecode + +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.activity.ComponentActivity +import io.embrace.android.exampleapp.R + +class BytecodeViewClickActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_bytecode_view_click) + + findViewById(R.id.btn_bytecode_view_click).setOnClickListener(::onClick) + findViewById(R.id.btn_bytecode_view_click).setOnLongClickListener(::onLongClick) + } + + private fun onClick(v: View) { + Log.d("BytecodeViewClickActivity", "Button clicked : ${v.id}") + } + + private fun onLongClick(v: View): Boolean { + Log.d("BytecodeViewClickActivity", "Button long clicked : ${v.id}") + return false + } +} diff --git a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeWebViewActivity.kt b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeWebViewActivity.kt new file mode 100644 index 0000000000..bab1ac36f0 --- /dev/null +++ b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/BytecodeWebViewActivity.kt @@ -0,0 +1,26 @@ +package io.embrace.android.exampleapp.ui.examples.bytecode + +import android.graphics.Bitmap +import android.os.Bundle +import android.util.Log +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.activity.ComponentActivity + +class BytecodeWebViewActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val webView = WebView(this) + webView.webViewClient = MyWebviewClient() + setContentView(webView) + webView.loadUrl("https://embrace.io/") + } +} + +private class MyWebviewClient : WebViewClient() { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + Log.d("BytecodeWebViewActivity", "Page started loading: $url") + } +} diff --git a/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/ExamplePushNotificationService.kt b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/ExamplePushNotificationService.kt new file mode 100644 index 0000000000..b9b8af904c --- /dev/null +++ b/examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/examples/bytecode/ExamplePushNotificationService.kt @@ -0,0 +1,15 @@ +package io.embrace.android.exampleapp.ui.examples.bytecode + +import android.annotation.SuppressLint +import android.util.Log +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage + +@SuppressLint("MissingFirebaseInstanceTokenRefresh") +class ExamplePushNotificationService : FirebaseMessagingService() { + + override fun onMessageReceived(message: RemoteMessage) { + super.onMessageReceived(message) + Log.d("EmbraceExample", "Message received: ${message.data}") + } +} diff --git a/examples/ExampleApp/app/src/main/res/layout/activity_bytecode_fcm_push_notification.xml b/examples/ExampleApp/app/src/main/res/layout/activity_bytecode_fcm_push_notification.xml new file mode 100644 index 0000000000..fc26dcb05d --- /dev/null +++ b/examples/ExampleApp/app/src/main/res/layout/activity_bytecode_fcm_push_notification.xml @@ -0,0 +1,27 @@ + + + + + +