| # Life of an Android Resource |
| |
| [TOC] |
| |
| ## Overview |
| |
| This document describes how [Android Resources][android resources] |
| are built in Chromium's build system. It does not mention native resources |
| which are [processed differently][native resources]. |
| |
| [android resources]: https://developer.android.com/guide/topics/resources/providing-resources |
| [native resources]: https://www.chromium.org/developers/tools-we-use-in-chromium/grit/grit-users-guide |
| |
| The steps consume the following files as inputs: |
| * `AndroidManifest.xml` |
| * Including `AndroidManifest.xml` files from libraries, which get merged |
| together |
| * res/ directories |
| |
| The steps produce the following intermediate files: |
| * `R.srcjar` (contains `R.java` files) |
| * `R.txt` |
| * `.resources.zip` |
| |
| The steps produce the following files within an `.apk`: |
| * `AndroidManifest.xml` (a binary xml file) |
| * `resources.arsc` (contains all values and configuration metadata) |
| * `res/**` (drawables and layouts) |
| * `classes.dex` (just a small portion of classes from generated `R.java` files) |
| |
| |
| ## The Build Steps |
| |
| Whenever you try to compile an apk or library target, resources go through the |
| following steps: |
| |
| ### 1. Constructs .build\_config files: |
| |
| Inputs: |
| * GN target metadata |
| * Other `.build_config.json` files |
| |
| Outputs: |
| * Target-specific `.build_config.json` file |
| |
| `write_build_config.py` is run to record target metadata needed by future steps. |
| For more details, see [build_config.md](build_config.md). |
| |
| |
| ### 2. Prepares resources: |
| |
| Inputs: |
| * Target-specific `.build_config.json` file |
| * Files listed as `sources` |
| |
| Outputs: |
| * Target-specific `resources.zip` (contains all resources listed in `sources`). |
| * Target-specific `R.txt` (list of all resources, including dependencies). |
| |
| `prepare_resources.py` zips up the target-specific resource files and generates |
| `R.txt`. No optimizations, crunching, etc are done on the resources. |
| |
| **The following steps apply only to apk & bundle targets (not to library |
| targets).** |
| |
| ### 3. Create target-specific R.java files |
| |
| Inputs: |
| * `R.txt` from dependencies. |
| |
| Outputs: |
| * Target-specific (placeholder) `R.java` file. |
| |
| A target-specific `R.java` is generated for each `android_library()` target that |
| sets `resources_package`. Resource IDs are not known at this phase, so all |
| values are set as placeholders. This copy of `R` classes are discarded and |
| replaced with new copies at step 4. |
| |
| Example placeholder R.java file: |
| ```java |
| package org.chromium.mypackage; |
| |
| public final class R { |
| public static class anim { |
| public static int abc_fade_in = 0; |
| public static int abc_fade_out = 0; |
| ... |
| } |
| ... |
| } |
| ``` |
| |
| ### 4. Finalizes apk resources: |
| |
| Inputs: |
| * Target-specific `.build_config.json` file |
| * Dependencies' `R.txt` files |
| * Dependencies' `resources.zip` files |
| |
| Output: |
| * Packaged `resources zip` (named `foo.ap_`) containing: |
| * `AndroidManifest.xml` (as binary xml) |
| * `resources.arsc` |
| * `res/**` |
| * Final `R.txt` |
| * Contains a list of resources and their ids (including of dependencies). |
| * Final `R.java` files |
| * See [What are `R.java` files and how are they generated]( |
| #how-r_java-files-are-generated) |
| |
| |
| #### 4(a). Compiles resources: |
| |
| For each library / resources target your apk depends on, the following happens: |
| * Use a regex (defined in the apk target) to remove select resources (optional). |
| * Convert png images to webp for binary size (optional). |
| * Move drawables in mdpi to non-mdpi directory ([why?](http://crbug.com/289843)) |
| * Use `aapt2 compile` to compile xml resources to binary xml (references to |
| other resources will now use the id rather than the name for faster lookup at |
| runtime). |
| * `aapt2 compile` adds headers/metadata to 9-patch images about which parts of |
| the image are stretchable vs static. |
| * `aapt2 compile` outputs a zip with the compiled resources (one for each |
| dependency). |
| |
| |
| #### 4(b). Links resources: |
| |
| After each dependency is compiled into an intermediate `.zip`, all those zips |
| are linked by the `aapt2 link` command which does the following: |
| * Use the order of dependencies supplied so that some resources clober each |
| other. |
| * Compile the `AndroidManifest.xml` to binary xml (references to resources are |
| now using ids rather than the string names) |
| * Create a `resources.arsc` file that has the name and values of string |
| resources as well as the name and path of non-string resources (ie. layouts |
| and drawables). |
| * Combine the compiled resources into one packaged resources apk (a zip file |
| with an `.ap_` extension) that has all the resources related files. |
| |
| |
| #### 4(c). Optimizes resources: |
| |
| Targets can opt into the following optimizations: |
| 1) Resource name collapsing: Maps all resources to the same name. Access to |
| resources via `Resources.getIdentifier()` no longer work unless resources are |
| [allowlisted](#adding-resources-to-the-allowlist). |
| 2) Resource filename obfuscation: Renames resource file paths from e.g.: |
| `res/drawable/something.png` to `res/a`. Rename mapping is stored alongside |
| APKs / bundles in a `.pathmap` file. Renames are based on hashes, and so are |
| stable between builds (unless a new hash collision occurs). |
| 3) Unused resource removal: Referenced resources are extracted from the |
| optimized `.dex` and `AndroidManifest.xml`. Resources that are directly or |
| indirectly used by these files are removed. |
| |
| ## App Bundles and Modules: |
| |
| Processing resources for bundles and modules is slightly different. Each module |
| has its resources compiled and linked separately (ie: it goes through the |
| entire process for each module). The modules are then combined to form a |
| bundle. Moreover, during "Finalizing the apk resources" step, bundle modules |
| produce a `resources.proto` file instead of a `resources.arsc` file. |
| |
| Resources in a dynamic feature module may reference resources in the base |
| module. During the link step for feature module resources, the linked resources |
| of the base module are passed in. However, linking against resources currently |
| works only with `resources.arsc` format. Thus, when building the base module, |
| resources are compiled as both `resources.arsc` and `resources.proto`. |
| |
| ## Debugging resource related errors when resource names are obfuscated |
| |
| An example message from a stacktrace could be something like this: |
| ``` |
| java.lang.IllegalStateException: Could not find CoordinatorLayout descendant |
| view with id org.chromium.chrome:id/0_resource_name_obfuscated to anchor view |
| android.view.ViewStub{be192d5 G.E...... ......I. 0,0-0,0 #7f0a02ad |
| app:id/0_resource_name_obfuscated} |
| ``` |
| |
| `0_resource_name_obfuscated` is the resource name for all resources that had |
| their name obfuscated/stripped during the optimize resources step. To help with |
| debugging, the `R.txt` file is archived. The `R.txt` file contains a mapping |
| from resource ids to resource names and can be used to get the original resource |
| name from the id. In the above message the id is `0x7f0a02ad`. |
| |
| For local builds, `R.txt` files are output in the `out/*/apks` directory. |
| |
| For official builds, Googlers can get archived `R.txt` files next to archived |
| apks. |
| |
| ### Adding resources to the allowlist |
| |
| If a resource is accessed via `getIdentifier()` it needs to be allowed by an |
| aapt2 resources config file. The config file looks like this: |
| |
| ``` |
| <resource type>/<resource name>#no_obfuscate |
| ``` |
| eg: |
| ``` |
| string/app_name#no_obfuscate |
| id/toolbar#no_obfuscate |
| ``` |
| |
| The aapt2 config file is passed to the ninja target through the |
| `resources_config_paths` variable. To add a resource to the allowlist, check |
| where the config is for your target and add a new line for your resource. If |
| none exist, create a new config file and pass its path in your target. |
| |
| ### Webview resource ids |
| |
| The first two bytes of a resource id is the package id. For regular apks, this |
| is `0x7f`. However, Webview is a shared library which gets loaded into other |
| apks. The package id for webview resources is assigned dynamically at runtime. |
| When webview is loaded it calls this [R file's][Base Module R.java File] |
| `onResourcesLoaded()` function to have the correct package id. When |
| deobfuscating webview resource ids, disregard the first two bytes in the id when |
| looking it up in the `R.txt` file. |
| |
| Monochrome, when loaded as webview, rewrites the package ids of resources used |
| by the webview portion to the correct value at runtime, otherwise, its resources |
| have package id `0x7f` when run as a regular apk. |
| |
| [Base Module R.java File]: https://cs.chromium.org/chromium/src/out/android-Debug/gen/android_webview/system_webview_apk/generated_java/gen/base_module/R.java |
| |
| ## How R.java files are generated |
| |
| `R.java` contain a set of nested static classes, each with static fields |
| containing ids. These ids are used in java code to reference resources in |
| the apk. |
| |
| There are three types of `R.java` files in Chrome. |
| 1. Root / Base Module `R.java` Files |
| 2. DFM `R.java` Files |
| 3. Per-Library `R.java` Files |
| |
| ### Root / Base Module `R.java` Files |
| Contain base android resources. All `R.java` files can access base module |
| resources through inheritance. |
| |
| Example Root / Base Module `R.java` File: |
| ```java |
| package gen.base_module; |
| |
| public final class R { |
| public static class anim { |
| public static int abc_fade_in = 0x7f010000; |
| public static int abc_fade_out = 0x7f010001; |
| public static int abc_slide_in_top = 0x7f010007; |
| } |
| public static class animator { |
| public static int design_appbar_state_list_animator = 0x7f020000; |
| } |
| } |
| ``` |
| |
| ### DFM `R.java` Files |
| Extend base module root `R.java` files. This allows DFMs to access their own |
| resources as well as the base module's resources. |
| |
| Example DFM Root `R.java` File |
| ```java |
| package gen.vr_module; |
| |
| public final class R { |
| public static class anim extends gen.base_module.R.anim { |
| } |
| public static class animator extends gen.base_module.R.animator { |
| // Each DFM uses a unique package byte (here it's 7e rather than 7f) |
| public static int design_appbar_state_list_animator = 0x7e020000; |
| } |
| } |
| ``` |
| |
| *** note |
| **Note:** Since some Android APIs (E.g. notification icons) assume resources to |
| be in the base module, we currently move all DFM resources to the base module |
| as a build step. |
| *** |
| |
| |
| ### Per-Library `R.java` Files |
| Generated for each `android_library()` target that sets `resources_package`. |
| First a placeholder copy is generated in the `android_library()` step, and then |
| a final copy is created during finalization. |
| |
| Example final per-library `R.java`: |
| ```java |
| package org.chromium.chrome.vr; |
| |
| public final class R { |
| public static final class anim extends |
| gen.vr_module.R.anim {} |
| public static final class animator extends |
| gen.vr_module.R.animator {} |
| } |
| ``` |