Skip to content

Commit 06fa5d9

Browse files
committed
Inject React bundleId through ASM
1 parent 1758a1d commit 06fa5d9

File tree

20 files changed

+100
-139
lines changed

20 files changed

+100
-139
lines changed
Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,19 @@
11
package io.embrace.android.embracesdk.internal.buildinfo
22

3-
import android.content.res.Resources
4-
import io.embrace.android.embracesdk.internal.AndroidResourcesService
53
import io.embrace.android.embracesdk.internal.config.instrumented.schema.InstrumentedConfig
64

75
internal class BuildInfoServiceImpl(
86
private val instrumentedConfig: InstrumentedConfig,
9-
private val resources: AndroidResourcesService,
10-
private val packageName: String,
117
) : BuildInfoService {
128

13-
companion object {
14-
private const val BUILD_INFO_RN_BUNDLE_ID: String = "emb_rn_bundle_id"
15-
private const val RES_TYPE_STRING = "string"
16-
}
17-
189
private val info by lazy {
1910
BuildInfo(
2011
instrumentedConfig.project.getBuildId(),
2112
instrumentedConfig.project.getBuildType(),
2213
instrumentedConfig.project.getBuildFlavor(),
23-
getBuildResource(resources, packageName, BUILD_INFO_RN_BUNDLE_ID),
14+
instrumentedConfig.project.getReactNativeBundleId(),
2415
)
2516
}
2617

2718
override fun getBuildInfo(): BuildInfo = info
28-
29-
/**
30-
* Given a build property name and a build property type, retrieves the embrace build resource value.
31-
*/
32-
fun getBuildResource(
33-
resources: AndroidResourcesService,
34-
packageName: String,
35-
buildProperty: String,
36-
): String? {
37-
return try {
38-
val resourceId =
39-
resources.getIdentifier(buildProperty, RES_TYPE_STRING, packageName)
40-
resources.getString(resourceId)
41-
} catch (ex: NullPointerException) {
42-
throw IllegalArgumentException(
43-
"No resource found for $buildProperty property. Failed to create build info.",
44-
ex
45-
)
46-
} catch (ex: Resources.NotFoundException) {
47-
null
48-
}
49-
}
5019
}

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/CoreModuleImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ class CoreModuleImpl(
4040
}
4141

4242
override val buildInfoService: BuildInfoService by lazy {
43-
BuildInfoServiceImpl(initModule.instrumentedConfig, resources, context.packageName)
43+
BuildInfoServiceImpl(initModule.instrumentedConfig)
4444
}
4545
}

embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/ProjectConfig.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,11 @@ interface ProjectConfig {
3939
* This is not possible to specify in the embrace-config.json.
4040
*/
4141
fun getBuildFlavor(): String? = null
42+
43+
/**
44+
* The project's React Native bundleId
45+
*
46+
* This is not possible to specify in the embrace-config.json.
47+
*/
48+
fun getReactNativeBundleId(): String? = null
4249
}

embrace-bytecode-instrumentation-tests/src/test/java/io/embrace/gradle/plugin/instrumentation/FakeBytecodeInstrumentationParams.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class FakeBytecodeInstrumentationParams(
1818
get() = TODO("Not yet implemented")
1919
override val encodedSharedObjectFilesMap: RegularFileProperty
2020
get() = TODO("Not yet implemented")
21+
override val reactNativeBundleId: RegularFileProperty
22+
get() = TODO("Not yet implemented")
2123
override val classInstrumentationFilter: Property<ClassInstrumentationFilter>
2224
get() = TODO("Not yet implemented")
2325
}

embrace-gradle-plugin-integration-tests/fixtures/react-native-android/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ android {
109109
// Caution! In production, you need to generate your own keystore file.
110110
// see https://reactnative.dev/docs/signed-apk-android.
111111
signingConfig signingConfigs.debug
112-
minifyEnabled enableProguardInReleaseBuilds
112+
minifyEnabled true
113113
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
114114
}
115115
}

embrace-gradle-plugin-integration-tests/fixtures/rn-upload-simple/build.gradle

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import io.embrace.android.gradle.plugin.tasks.reactnative.EmbraceRnSourcemapGeneratorTask
1+
import io.embrace.android.gradle.plugin.tasks.reactnative.GenerateRnSourcemapTask
22
import io.embrace.android.gradle.plugin.network.EmbraceEndpoint
33

44
plugins {
@@ -7,7 +7,7 @@ plugins {
77
id("io.embrace.android.testplugin")
88
}
99

10-
project.tasks.register("testTask", EmbraceRnSourcemapGeneratorTask) { task ->
10+
project.tasks.register("testTask", GenerateRnSourcemapTask) { task ->
1111
integrationTest.configureGradleUploadTask(project, task, EmbraceEndpoint.SOURCE_MAP, "sourcemap.json")
1212

1313
task.generatedEmbraceResourcesDirectory.set(
@@ -22,4 +22,7 @@ project.tasks.register("testTask", EmbraceRnSourcemapGeneratorTask) { task ->
2222
task.sourcemapAndBundleFile.set(
2323
project.layout.buildDirectory.file("output")
2424
)
25+
task.bundleIdOutputFile.set(
26+
project.layout.buildDirectory.file("bundleIdFile.txt")
27+
)
2528
}

embrace-gradle-plugin-integration-tests/src/test/java/io/embrace/android/gradle/integration/testcases/RnSourcemapTaskIntegrationTest.kt renamed to embrace-gradle-plugin-integration-tests/src/test/java/io/embrace/android/gradle/integration/testcases/GenerateRnSourcemapTaskIntegrationTest.kt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import com.squareup.moshi.JsonClass
44
import io.embrace.android.gradle.integration.framework.IntegrationTestDefaults
55
import io.embrace.android.gradle.integration.framework.PluginIntegrationTestRule
66
import io.embrace.android.gradle.integration.framework.buildFile
7-
import io.embrace.android.gradle.integration.framework.file
87
import io.embrace.android.gradle.network.FormPart
98
import io.embrace.android.gradle.network.validateBodyApiToken
109
import io.embrace.android.gradle.network.validateBodyAppId
@@ -20,7 +19,7 @@ import org.junit.Rule
2019
import org.junit.Test
2120
import java.io.File
2221

23-
class RnSourcemapTaskIntegrationTest {
22+
class GenerateRnSourcemapTaskIntegrationTest {
2423

2524
@Rule
2625
@JvmField
@@ -31,19 +30,14 @@ class RnSourcemapTaskIntegrationTest {
3130
rule.runTest(
3231
fixture = "rn-upload-simple",
3332
assertions = { projectDir ->
34-
// 1. assert that RN bundle ID was injected as a string resource
35-
val symbols = projectDir.buildFile("generated-embrace-resources/values/rn_sourcemap.xml")
36-
val expectedResXml = projectDir.file("expected/rn_sourcemap.xml").readText()
37-
assertEquals(expectedResXml, symbols.readText())
38-
39-
// 2. assert that the task output was the bundle + sourcemap file zipped as JSON
33+
// 1. assert that the task output was the bundle + sourcemap file zipped as JSON
4034
verifyOutputFile(projectDir.buildFile("output"))
4135

42-
// 3. assert that a request took place
36+
// 2. assert that a request took place
4337
val request = fetchRequest(EmbraceEndpoint.SOURCE_MAP)
4438
assertHeaders(request, "multipart/form-data", "abcde")
4539

46-
// 4. assert the multipart form data contains bundle info
40+
// 3. assert the multipart form data contains bundle info
4741
val parts: List<FormPart> = readMultipartRequest(request)
4842
assertEquals(4, parts.size)
4943
parts[0].validateBodyAppId(IntegrationTestDefaults.APP_ID)

embrace-gradle-plugin-integration-tests/src/test/java/io/embrace/android/gradle/integration/testcases/ReactNativeAndroidTest.kt

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package io.embrace.android.gradle.integration.testcases
22

3-
import io.embrace.android.gradle.integration.framework.ApkDisassembler
43
import io.embrace.android.gradle.integration.framework.PluginIntegrationTestRule
5-
import org.junit.Assert.assertNotNull
64
import org.junit.Rule
75
import org.junit.Test
86
import java.io.File
@@ -46,23 +44,15 @@ class ReactNativeAndroidTest {
4644
installNodeModules(projectDir)
4745
setupMockResponses(handshakeLibs, handshakeArchs, defaultExpectedVariants)
4846
},
49-
assertions = { projectDir ->
47+
assertions = {
5048
verifyBuildTelemetryRequestSent(defaultExpectedVariants)
5149
verifyHandshakes(defaultExpectedLibs, defaultExpectedArchs, defaultExpectedVariants)
5250
verifyUploads(handshakeLibs, handshakeArchs, defaultExpectedVariants)
53-
54-
val filename = "app/build/outputs/apk/release/app-release.apk"
55-
verifyBundleIdInjection(File(projectDir, filename))
51+
// verifyAsmInjection(projectDir)
5652
}
5753
)
5854
}
5955

60-
private fun verifyBundleIdInjection(apkFile: File) {
61-
val decodedApk = ApkDisassembler().disassembleApk(apkFile)
62-
val bundleId = decodedApk.getStringResource("emb_rn_bundle_id")
63-
assertNotNull(bundleId)
64-
}
65-
6656
private fun installNodeModules(projectDir: File) {
6757
val process = ProcessBuilder("npm", "install")
6858
.directory(projectDir)
@@ -76,4 +66,20 @@ class ReactNativeAndroidTest {
7666
error("npm install failed with exit code $exitCode\n$output")
7767
}
7868
}
69+
70+
// private fun verifyAsmInjection(projectDir: File) {
71+
// // Read and parse the smali file containing the injected symbols
72+
// val smaliFile = SmaliConfigReader().readSmaliFiles(
73+
// File(projectDir, "app"),
74+
// listOf("/io/embrace/android/embracesdk/internal/config/instrumented/ProjectConfigImpl")
75+
// ).first()
76+
//
77+
// // Get the return value of the getBase64SharedObjectFilesMap method
78+
// val method = SmaliParser().parse(
79+
// smaliFile,
80+
// listOf(SmaliMethod("getReactNativeBundleId()Ljava/lang/String;"))
81+
// ).methods.first()
82+
//
83+
// assertEquals("B3338A366B74EB89C112C8BB13C1B570", method.returnValue)
84+
// }
7985
}

embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/AsmTaskRegistration.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.android.build.api.instrumentation.FramesComputationMode
44
import com.android.build.api.instrumentation.InstrumentationScope
55
import io.embrace.android.gradle.plugin.gradle.tryGetTaskProvider
66
import io.embrace.android.gradle.plugin.tasks.ndk.EncodeFileToBase64Task
7+
import io.embrace.android.gradle.plugin.tasks.reactnative.GenerateRnSourcemapTask
78
import io.embrace.android.gradle.plugin.tasks.registration.EmbraceTaskRegistration
89
import io.embrace.android.gradle.plugin.tasks.registration.RegistrationParams
910
import io.embrace.android.gradle.plugin.util.capitalizedString
@@ -56,6 +57,15 @@ class AsmTaskRegistration : EmbraceTaskRegistration {
5657
it.outputFile
5758
}
5859
)
60+
61+
val reactNativeTask = project.tryGetTaskProvider(
62+
"${GenerateRnSourcemapTask.NAME}${data.name.capitalizedString()}",
63+
GenerateRnSourcemapTask::class.java
64+
) ?: return@afterEvaluate
65+
66+
asmTransformationTask.configure { it.dependsOn(reactNativeTask) }
67+
68+
params.reactNativeBundleId.set(reactNativeTask.flatMap { it.bundleIdOutputFile })
5969
}
6070
}
6171
} catch (exception: Exception) {

embrace-gradle-plugin/src/main/java/io/embrace/android/gradle/plugin/instrumentation/BytecodeInstrumentationParams.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ interface BytecodeInstrumentationParams : InstrumentationParameters {
3333
@get:PathSensitive(PathSensitivity.NONE)
3434
val encodedSharedObjectFilesMap: RegularFileProperty
3535

36+
/**
37+
* React native bundle ID to be injected in the SDK.
38+
*/
39+
@get:Optional
40+
@get:InputFiles
41+
@get:PathSensitive(PathSensitivity.NONE)
42+
val reactNativeBundleId: RegularFileProperty
43+
3644
/**
3745
* Whether or not the plugin should operate for this variant.
3846
*/

0 commit comments

Comments
 (0)