1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297

build / android / docs / life_of_a_resource.md [blame]

# 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 {}
}
```