Replace files in BUNDLE-METADATA folder in Android App Bundles (.aab) file

233 views Asked by At

How to replace files in an existing directory in BUNDLE-METADATA folder in Android App Bundles (.aab) file?

For desymbolication of native crashes, we want to replace the existing .dbg file in the path="/BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a with the file we receive from other sources. These existing .dbg is created during build process. For some reason, using the .dbg created during the build is not able to help with desymboliction both locally as well as in play dev console. Only when we upload the original .so file to play dev console, the desymbolication happens correctly.

This API seems to work only for adding new files and directory. https://developer.android.com/reference/tools/gradle-api/8.3/null/com/android/build/api/variant/BundleConfig

Is it possible to replace existing files using AGP 7.3?

1

There are 1 answers

4
VonC On

In your scenario, you have an Android App Bundle (.aab) file and within it, there is a specific .dbg file located in the BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a directory.

Your goal would be to replace this existing .dbg file with another one from a different source to aid in desymbolication of native crashes, as the current .dbg file is not serving its purpose.

+----------------------------+
| Android App Bundle (.aab)  |
| +------------------------+ |
| | BUNDLE-METADATA        | |
| | +--------------------+ | |
| | | debugsymbols       | | |
| | | +---------------+  | | |
| | | | arm64-v8a     |  | | |
| | | | +-----------+ |  | | |
| | | | | new.dbg   | |  | | | <-- Replaced .dbg file
| | | | +-----------+ |  | | |
| | | +---------------+  | | |
| | +--------------------+ | |
| +------------------------+ |
+----------------------------+

I do not see any support for that.

Modifying a file in an existing Android App Bundle (AAB) can indeed impact its signature, potentially rendering it invalid. When you create an AAB, it is signed with a key that ensures its integrity and authenticity. Any subsequent modifications to the AAB, such as replacing a file, would alter the bundle's content, thus changing its signature.

There is also an optional code signing and verification mechanism called "Code Transparency" for apps published with Android App Bundles. Although it operates independently of the signing scheme used for app bundles and APKs, it emphasizes the importance of signing as a verification mechanism.


I have full access to the build pipeline. So, if there is any way that can be used before the app bundle is signed and packaged, that is what I am looking for.

Mainly, I am looking to replace the generated files just before the final packaging of app bundles is done. Just before the BUNDLE-METADATA folder is created.

In that case, the optimal approach would be to replace the .dbg file just before the final packaging and signing of the app bundle occurs. The integrity and the signature of the app bundle remain intact.

Build Pipeline Steps:

+---------------------+      +---------------------+
| Initial Build Steps | ---> | Generate .dbg files |
+---------------------+      +---------------------+
                                     |
                                     V
     +----------------------------------+      +--------------------------+
     | Replace .dbg with external file  | ---> | Final Packaging & Signing|
     +----------------------------------+      +--------------------------+
  • Build your app as usual up until the point where the .dbg file is generated.

  • Replace the generated .dbg file with the external .dbg file you have (script or simple cp step):

    cp path/to/external.dbg path/to/build/intermediates/debugSymbols/arm64-v8a/new.dbg
    

    That could be done through a custom gradle task or by writing a Gradle plugin

  • Continue with the final packaging and signing** of the app bundle. The BUNDLE-METADATA folder and its contents, including the replaced .dbg file, would then be packaged and signed as part of the normal build process.

./gradlew bundleRelease

That way, you make sure the replacement of the .dbg file happens before the final packaging and signing of the app bundle, thus maintaining the integrity and the signature of the app bundle.


For instance: extending the Android Gradle Plugin (AGP) could include the "replacing files before the final packaging of the app bundle" step.

apply plugin: 'com.android.application'

android {
    ...
}

// Define a custom task to replace the .dbg file
task replaceDbgFile {
    doLast {
        println 'Replacing .dbg file...'
        def command = ['cp', 'path/to/external.dbg', 'path/to/build/intermediates/debugSymbols/arm64-v8a/new.dbg']
        command.execute()
        println '.dbg file replaced successfully.'
    }
}

// Make sure the custom task is executed before the bundleRelease task
gradle.tasks.named('bundleRelease').configure {
    dependsOn replaceDbgFile
}

The new task named replaceDbgFile uses the Groovy execute() method to run the cp command, replacing the generated .dbg file with the external .dbg file.
The gradle.tasks.named('bundleRelease').configure { dependsOn replaceDbgFile } line makes sure the replaceDbgFile task is executed before the bundleRelease task, which packages and signs the app bundle.


The OP Nitin Sethi adds in the comments:

I ended up using dependsOn for my new task that does the copying of right so files right after the task "extractReleaseNativeDebugMetadata" or equivalent.

Do you know of a better way to do copy and remove using Groovy DSL APIs?

Your dependsOn approach is a sound method to make sure the custom task executes at the appropriate point in the build process, specifically after the extractReleaseNativeDebugMetadata task. That way, the required files are in place before proceeding to the next steps in the build pipeline.
See also "A Gradle quickie: properly using dependsOn", from Cédric Champeau.

For copying and removing files using the Groovy DSL within Gradle, there are more idiomatic ways that leverage Gradle's built-in task types and methods:

You can use Gradle's Copy task type to define a task for copying files.

task copyDbgFile(type: Copy) {
    from 'path/to/external.dbg'
    into 'path/to/build/intermediates/debugSymbols/arm64-v8a'
    rename { String fileName ->
        'new.dbg'  // Rename if necessary
    }
}

Use Gradle's Delete task type to define a task for deleting files.

task deleteOldDbgFile(type: Delete) {
    delete 'path/to/build/intermediates/debugSymbols/arm64-v8a/old.dbg'
}

Make sure the copy and delete tasks are executed at the correct point in the build process.

gradle.tasks.named('extractReleaseNativeDebugMetadata').configure {
    finalizedBy copyDbgFile, deleteOldDbgFile
}