Skip to content

Commit e8397c2

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

File tree

20 files changed

+103
-138
lines changed

20 files changed

+103
-138
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
@@ -116,7 +116,7 @@ android {
116116
}
117117

118118
embrace {
119-
autoAddEmbraceDependencies.set(false)
119+
autoAddEmbraceDependencies.set(true)
120120
}
121121

122122
dependencies {

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: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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
4+
import io.embrace.android.gradle.integration.framework.smali.SmaliConfigReader
5+
import io.embrace.android.gradle.integration.framework.smali.SmaliMethod
6+
import io.embrace.android.gradle.integration.framework.smali.SmaliParser
7+
import org.junit.Assert.assertEquals
68
import org.junit.Rule
79
import org.junit.Test
810
import java.io.File
@@ -50,19 +52,11 @@ class ReactNativeAndroidTest {
5052
verifyBuildTelemetryRequestSent(defaultExpectedVariants)
5153
verifyHandshakes(defaultExpectedLibs, defaultExpectedArchs, defaultExpectedVariants)
5254
verifyUploads(handshakeLibs, handshakeArchs, defaultExpectedVariants)
53-
54-
val filename = "app/build/outputs/apk/release/app-release.apk"
55-
verifyBundleIdInjection(File(projectDir, filename))
55+
verifyAsmInjection(projectDir)
5656
}
5757
)
5858
}
5959

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-
6660
private fun installNodeModules(projectDir: File) {
6761
val process = ProcessBuilder("npm", "install")
6862
.directory(projectDir)
@@ -76,4 +70,20 @@ class ReactNativeAndroidTest {
7670
error("npm install failed with exit code $exitCode\n$output")
7771
}
7872
}
73+
74+
private fun verifyAsmInjection(projectDir: File) {
75+
// Read and parse the smali file containing the injected symbols
76+
val smaliFile = SmaliConfigReader().readSmaliFiles(
77+
File(projectDir, "app"),
78+
listOf("/io/embrace/android/embracesdk/internal/config/instrumented/ProjectConfigImpl")
79+
).first()
80+
81+
// Get the return value of the getBase64SharedObjectFilesMap method
82+
val method = SmaliParser().parse(
83+
smaliFile,
84+
listOf(SmaliMethod("getReactNativeBundleId()Ljava/lang/String;"))
85+
).methods.first()
86+
87+
assertEquals("765FB008173DC25D016D112F67250241", method.returnValue)
88+
}
7989
}

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)