| # App Bundles and Dynamic Feature Modules (DFMs) | 
 |  | 
 | [TOC] | 
 |  | 
 | ## About Bundles | 
 | [Android App bundles] is a Play Store feature that allows packaging an app as | 
 | multiple `.apk` files, known as "splits". Bundles are zip files with an `.aab` | 
 | extension. See [android_build_instructions.md#multiple-chrome-targets] for a | 
 | list of buildable bundle targets. | 
 |  | 
 | Bundles provide three main advantages over monolithic `.apk` files: | 
 | 1. Language resources are split into language-specific `.apk` files, known as | 
 |    "resource splits". Delivering only the active languages reduces the overhead | 
 |    of UI strings. | 
 |    * Resource splits can also be made on a per-screen-density basis (for drawables), | 
 |      but Chrome has not taken advantage of this (yet). | 
 | 2. Features can be packaged into lazily loaded `.apk` files, known as | 
 |    "feature splits". Chrome enables [isolated splits], which means feature | 
 |    splits have no performance overhead until used (on Android O+ at least). | 
 | 3. Feature splits can be downloaded on-demand, saving disk space for users that | 
 |    do not need the functionality they provide. These are known as | 
 |    "Dynamic feature modules", or "DFMs". | 
 |    * **The install experience for DFMs is quite poor (5-30 seconds install times, | 
 |      sometimes fails, sometimes [triggers a crash]).** | 
 |  | 
 | You can inspect which `.apk` files are produced by a bundle target via: | 
 | ``` | 
 | out/Default/bin/${target_name} build-bundle-apks --output-apks foo.apks | 
 | unzip -l foo.apks | 
 | ``` | 
 |  | 
 | *** note | 
 | Adding new features via feature splits is highly encouraged when it makes sense | 
 | to do so: | 
 |  * Has a non-trivial amount of Java code (after optimization). E.g. >150kb | 
 |  * Not needed on startup | 
 |  * Has a small integration surface (calls into it must be done with reflection) | 
 |  * Not used by WebView | 
 | *** | 
 |  | 
 | [android_build_instructions.md#multiple-chrome-targets]: android_build_instructions.md#multiple-chrome-targets | 
 | [Android App Bundles]: https://developer.android.com/guide/app-bundle | 
 | [isolated splits]: android_isolated_splits.md | 
 | [triggers a crash]: https://chromium.googlesource.com/chromium/src/+/main/docs/android_isolated_splits.md#Conflicting-ClassLoaders-2 | 
 |  | 
 | ### Declaring App Bundles with GN Templates | 
 |  | 
 | Here's an example that shows how to declare a simple bundle that contains a | 
 | single base module, which enables language-based splits: | 
 |  | 
 | ```gn | 
 |   android_app_bundle_module("foo_base_module") { | 
 |     # Declaration are similar to android_apk here. | 
 |     ... | 
 |   } | 
 |  | 
 |   android_app_bundle("foo_bundle") { | 
 |     base_module_target = ":foo_base_module" | 
 |  | 
 |     # The name of our bundle file (without any suffix). | 
 |     bundle_name = "FooBundle" | 
 |  | 
 |     # Enable language-based splits for this bundle. Which means that | 
 |     # resources and assets specific to a given language will be placed | 
 |     # into their own split APK in the final .apks archive. | 
 |     enable_language_splits = true | 
 |  | 
 |     # Proguard settings must be passed at the bundle, not module, target. | 
 |     proguard_enabled = !is_java_debug | 
 |   } | 
 | ``` | 
 |  | 
 | When generating the `foo_bundle` target with Ninja, you will end up with | 
 | the following: | 
 |  | 
 |   * The bundle file under `out/Release/apks/FooBundle.aab` | 
 |  | 
 |   * A helper script called `out/Release/bin/foo_bundle`, which can be used | 
 |     to install / launch / uninstall the bundle on local devices. | 
 |  | 
 |     This works like an APK wrapper script (e.g. `foo_apk`). Use `--help` | 
 |     to see all possible commands supported by the script. | 
 |  | 
 |  | 
 | The remainder of this doc focuses on DFMs. | 
 |  | 
 | ## Declaring Dynamic Feature Modules (DFMs) | 
 |  | 
 | This guide walks you through the steps to create a DFM called _Foo_ and add it | 
 | to the Chrome bundles. | 
 |  | 
 | *** note | 
 | **Note:** To make your own module you'll essentially have to replace every | 
 | instance of `foo`/`Foo`/`FOO` with `your_feature_name`/`YourFeatureName`/ | 
 | `YOUR_FEATURE_NAME`. | 
 | *** | 
 |  | 
 | ### Reference DFM | 
 |  | 
 | In addition to this guide, the | 
 | [Test Dummy](https://cs.chromium.org/chromium/src/chrome/android/modules/test_dummy/test_dummy_module.gni) | 
 | module serves as an actively-maintained reference DFM. Test Dummy is used in | 
 | automated bundle testing, and covers both Java and native code and resource | 
 | usage. | 
 |  | 
 | ### Create DFM target | 
 |  | 
 | DFMs are APKs. They have a manifest and can contain Java and native code as well | 
 | as resources. This section walks you through creating the module target in our | 
 | build system. | 
 |  | 
 | First, create the file | 
 | `//chrome/android/modules/foo/internal/java/AndroidManifest.xml` and add: | 
 |  | 
 | ```xml | 
 | <?xml version="1.0" encoding="utf-8"?> | 
 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 
 |     xmlns:dist="http://schemas.android.com/apk/distribution" | 
 |     featureSplit="foo"> | 
 |  | 
 |     <!-- dist:onDemand="true" makes this a separately installed module. | 
 |          dist:onDemand="false" would always install the module alongside the | 
 |          rest of Chrome. --> | 
 |     <dist:module | 
 |         dist:onDemand="true" | 
 |         dist:title="@string/foo_module_title"> | 
 |         <!-- This will fuse the module into the base APK if a system image | 
 |              APK is built from this bundle. --> | 
 |         <dist:fusing dist:include="true" /> | 
 |     </dist:module> | 
 |  | 
 |     <!-- Remove android:hasCode="false" when adding Java code. --> | 
 |     <application android:hasCode="false" /> | 
 | </manifest> | 
 | ``` | 
 |  | 
 | Next, create a descriptor configuring the Foo module. To do this, create | 
 | `//chrome/android/modules/foo/foo_module.gni` and add the following: | 
 |  | 
 | ```gn | 
 | foo_module_desc = { | 
 |   name = "foo" | 
 |   android_manifest = | 
 |       "//chrome/android/modules/foo/internal/java/AndroidManifest.xml" | 
 | } | 
 | ``` | 
 |  | 
 | Then, add the module descriptor to the appropriate descriptor list in | 
 | //chrome/android/modules/chrome_feature_modules.gni, e.g. the Chrome list: | 
 |  | 
 | ```gn | 
 | import("//chrome/android/modules/foo/foo_module.gni") | 
 | ... | 
 | chrome_module_descs += [ foo_module_desc ] | 
 | ``` | 
 |  | 
 | The next step is to add Foo to the list of feature modules for UMA recording. | 
 | For this, add `foo` to the `AndroidFeatureModuleName` in | 
 | `//tools/metrics/histograms/metadata/histogram_suffixes_list.xml`: | 
 |  | 
 | ```xml | 
 | <histogram_suffixes name="AndroidFeatureModuleName" ...> | 
 |   ... | 
 |   <suffix name="foo" label="Super Duper Foo Module" /> | 
 |   ... | 
 | </histogram_suffixes> | 
 | ``` | 
 |  | 
 | Lastly, give your module a title that Chrome and Play can use for the install | 
 | UI. To do this, add a string to | 
 | `//chrome/browser/ui/android/strings/android_chrome_strings.grd`: | 
 |  | 
 | ```xml | 
 | ... | 
 | <message name="IDS_FOO_MODULE_TITLE" | 
 |   desc="Text shown when the Foo module is referenced in install start, success, | 
 |         failure UI (e.g. in IDS_MODULE_INSTALL_START_TEXT, which will expand to | 
 |         'Installing Foo for Chrome…')."> | 
 |   Foo | 
 | </message> | 
 | ... | 
 | ``` | 
 |  | 
 | *** note | 
 | **Note:** This is for module title only. Other strings specific to the module | 
 | should go in the module, not here (in the base module). | 
 | *** | 
 |  | 
 | Congrats! You added the DFM Foo to Chrome. That is a big step but not very | 
 | useful so far. In the next sections you'll learn how to add code and resources | 
 | to it. | 
 |  | 
 |  | 
 | ### Building and installing modules | 
 |  | 
 | Before we are going to jump into adding content to Foo, let's take a look on how | 
 | to build and deploy the Monochrome bundle with the Foo DFM. The remainder of | 
 | this guide assumes the environment variable `OUTDIR` is set to a properly | 
 | configured GN build directory (e.g. `out/Debug`). | 
 |  | 
 | To build and install the Monochrome bundle to your connected device, run: | 
 |  | 
 | ```shell | 
 | $ autoninja -C $OUTDIR monochrome_public_bundle | 
 | $ $OUTDIR/bin/monochrome_public_bundle install -m foo | 
 | ``` | 
 |  | 
 | This will install the `Foo` module, the `base` module, and all modules with an | 
 | `AndroidManifest.xml` that: | 
 |  * Sets `<module dist:onDemand="false">`, or | 
 |  * Has `<dist:delivery>` conditions that are satisfied by the device being | 
 |    installed to. | 
 |  | 
 | *** note | 
 | **Note:** The install script may install more modules than you specify, e.g. | 
 | when there are default or conditionally installed modules (see | 
 | [below](#conditional-install) for details). | 
 | *** | 
 |  | 
 | You can then check that the install worked with: | 
 |  | 
 | ```shell | 
 | $ adb shell dumpsys package org.chromium.chrome | grep splits | 
 | >   splits=[base, config.en, foo] | 
 | ``` | 
 |  | 
 | Then try installing the Monochrome bundle without your module and print the | 
 | installed modules: | 
 |  | 
 | ```shell | 
 | $ $OUTDIR/bin/monochrome_public_bundle install | 
 | $ adb shell dumpsys package org.chromium.chrome | grep splits | 
 | >   splits=[base, config.en] | 
 | ``` | 
 |  | 
 | *** note | 
 | The wrapper script's `install` command does approximately: | 
 | ```sh | 
 | java -jar third_party/android_build_tools/bundletool/cipd/bundletool.jar build-apks --output tmp.apks ... | 
 | java -jar third_party/android_build_tools/bundletool/cipd/bundletool.jar install-apks --apks tmp.apks | 
 | ``` | 
 |  | 
 | The `install-apks` command uses `adb install-multiple` under-the-hood. | 
 | *** | 
 |  | 
 | ### Adding Java code | 
 |  | 
 | To make Foo useful, let's add some Java code to it. This section will walk you | 
 | through the required steps. | 
 |  | 
 | First, define a module interface for Foo. This is accomplished by adding the | 
 | `@ModuleInterface` annotation to the Foo interface. This annotation | 
 | automatically creates a `FooModule` class that can be used later to install and | 
 | access the module. To do this, add the following in the new file | 
 | `//chrome/browser/foo/android/java/src/org/chromium/chrome/browser/foo/Foo.java`: | 
 |  | 
 | ```java | 
 | package org.chromium.chrome.browser.foo; | 
 |  | 
 | import org.chromium.components.module_installer.builder.ModuleInterface; | 
 |  | 
 | /** Interface to call into Foo feature. */ | 
 | @ModuleInterface(module = "foo", impl = "org.chromium.chrome.browser.FooImpl") | 
 | public interface Foo { | 
 |     /** Magical function. */ | 
 |     void bar(); | 
 | } | 
 | ``` | 
 |  | 
 | Next, define an implementation that goes into the module in the new file | 
 | `//chrome/browser/foo/internal/android/java/src/org/chromium/chrome/browser/foo/FooImpl.java`: | 
 |  | 
 | ```java | 
 | package org.chromium.chrome.browser.foo; | 
 |  | 
 | import org.chromium.base.Log; | 
 |  | 
 | public class FooImpl implements Foo { | 
 |     @Override | 
 |     public void bar() { | 
 |         Log.i("FOO", "bar in module"); | 
 |     } | 
 | } | 
 | ``` | 
 |  | 
 | You can then use this provider to access the module if it is installed. To test | 
 | that, instantiate Foo and call `bar()` somewhere in Chrome: | 
 |  | 
 | ```java | 
 | if (FooModule.isInstalled()) { | 
 |     FooModule.getImpl().bar(); | 
 | } else { | 
 |     Log.i("FOO", "module not installed"); | 
 | } | 
 | ``` | 
 |  | 
 | The interface has to be available regardless of whether the Foo DFM is present. | 
 | Therefore, put those classes into the base module, creating a new public | 
 | build target in: `//chrome/browser/foo/BUILD.gn`: | 
 |  | 
 | ```gn | 
 | import("//build/config/android/rules.gni") | 
 |  | 
 | android_library("java") { | 
 |   sources = [ | 
 |     "android/java/src/org/chromium/chrome/browser/foo/Foo.java", | 
 |   ] | 
 |   deps = [ | 
 |     "//components/module_installer/android:module_installer_java", | 
 |     "//components/module_installer/android:module_interface_java", | 
 |   ] | 
 |   annotation_processor_deps = | 
 |     [ "//components/module_installer/android:module_interface_processor" ] | 
 | } | 
 | ``` | 
 |  | 
 | Then, depend on this target from where it is used as usual. For example, if the | 
 | caller is in `chrome_java in //chrome/android/BUILD.gn`: | 
 |  | 
 | ```gn | 
 | ... | 
 | android_library("chrome_java") { | 
 |   deps =[ | 
 |     ... | 
 |     "//chrome/browser/foo:java", | 
 |     ... | 
 |   ] | 
 | } | 
 | ... | 
 | ``` | 
 |  | 
 | The actual implementation, however, should go into the Foo DFM. For this | 
 | purpose, create a new file `//chrome/browser/foo/internal/BUILD.gn` and | 
 | make a library with the module Java code in it: | 
 |  | 
 | ```gn | 
 | import("//build/config/android/rules.gni") | 
 |  | 
 | android_library("java") { | 
 |   # Define like ordinary Java Android library. | 
 |   sources = [ | 
 |     "android/java/src/org/chromium/chrome/browser/foo/FooImpl.java", | 
 |     # Add other Java classes that should go into the Foo DFM here. | 
 |   ] | 
 |   deps = [ | 
 |     "//base:base_java", | 
 |     # Put other Chrome libs into the classpath so that you can call into them | 
 |     # from the Foo DFM. | 
 |     "//chrome/browser/bar:java", | 
 |     # The module can depend even on `chrome_java` due to factory magic, but this | 
 |     # is discouraged. Consider passing a delegate interface in instead. | 
 |     "//chrome/android:chrome_java", | 
 |     # Also, you'll need to depend on any //third_party or //components code you | 
 |     # are using in the module code. | 
 |   ] | 
 | } | 
 | ``` | 
 |  | 
 | Then, add this new library as a dependency of the Foo module descriptor in | 
 | `//chrome/android/modules/foo/foo_module.gni`: | 
 |  | 
 | ```gn | 
 | foo_module_desc = { | 
 |   ... | 
 |   java_deps = [ | 
 |     "//chrome/browser/foo/internal:java", | 
 |   ] | 
 | } | 
 | ``` | 
 |  | 
 | Finally, tell Android that your module is now containing code. Do that by | 
 | removing the `android:hasCode="false"` attribute from the `<application>` tag in | 
 | `//chrome/android/modules/foo/internal/java/AndroidManifest.xml`. You should be | 
 | left with an empty tag like so: | 
 |  | 
 | ```xml | 
 | ... | 
 |     <application /> | 
 | ... | 
 | ``` | 
 |  | 
 | Rebuild and install `monochrome_public_bundle`. Start Chrome and run through a | 
 | flow that tries to executes `bar()`. Depending on whether you installed your | 
 | module (`-m foo`) "`bar in module`" or "`module not installed`" is printed to | 
 | logcat. Yay! | 
 |  | 
 | ### Adding pre-built native libraries | 
 |  | 
 | You can add a third-party native library (or any standalone library that doesn't | 
 | depend on Chrome code) by adding it as a loadable module to the module descriptor in | 
 | `//chrome/android/moduiles/foo/foo_module.gni`: | 
 |  | 
 | ```gn | 
 | foo_module_desc = { | 
 |   ... | 
 |   loadable_modules_32_bit = [ "//path/to/32/bit/lib.so" ] | 
 |   loadable_modules_64_bit = [ "//path/to/64/bit/lib.so" ] | 
 | } | 
 | ``` | 
 |  | 
 | ### Adding Chrome native code | 
 |  | 
 | Chrome native code may be placed in a DFM. The easiest way to access native | 
 | feature code is by calling it from Java via JNI. When a module is first | 
 | accessed, its native library (or potentially libraries, if using a component | 
 | build), are automatically opened by the DFM framework, and a feature-specific | 
 | JNI method (supplied by the feature's implementation) is invoked. Hence, a | 
 | module's Java code may freely use JNI to call module native code. | 
 |  | 
 | Using the module framework and JNI to access the native code eliminates concerns | 
 | with DFM library file names (which vary across build variants), | 
 | `android_dlopen_ext()` (needed to open feature libraries), and use of dlsym(). | 
 |  | 
 | This mechanism can be extended if necessary by DFM implementers to facilitate | 
 | subsequent native-native calls, by having a JNI-called initialization method | 
 | create instance of a object or factory, and register it through a call to the | 
 | base module's native code (DFM native code can call base module code directly). | 
 |  | 
 | #### JNI | 
 |  | 
 | Read the `jni_generator` [docs](../third_party/jni_zero/README.md) before | 
 | reading this section. | 
 |  | 
 | There are some subtleties to how JNI registration works with DFMs: | 
 |  | 
 | * Generated wrapper `ClassNameJni` classes are packaged into the DFM's dex file | 
 | * The class containing the actual native definitions, | 
 |   `<module_name>_GEN_JNI.java`, is currently stored in the base module, but | 
 |   could be moved out | 
 | * The `Natives` interface you provide will need to be annotated with your module | 
 |   name as an argument to `NativeMethods`, eg. `@NativeMethods("foo")`, resulting | 
 |   in a uniquely named `foo_GEN_JNI.java` | 
 | * The DFM will need to provide a `generate_jni_registration` target | 
 |   that will generate all of the native registration functions | 
 |  | 
 | #### Calling DFM native code via JNI | 
 |  | 
 | A linker-assisted partitioning system automates the placement of code into | 
 | either the main Chrome library or feature-specific .so libraries. Feature code | 
 | may continue to make use of core Chrome code (eg. base::) without modification, | 
 | but Chrome must call feature code through a virtual interface (any "direct" | 
 | calls to the feature code from the main library will cause the feature code to | 
 | be pulled back into the main library). | 
 |  | 
 | Partitioning is explained in [Android Native | 
 | Libraries](android_native_libraries.md#partitioned-libraries). | 
 |  | 
 | First, build a module native interface. Supply a JNI method named | 
 | `JNI_OnLoad_foo` for the module framework to call, in | 
 | `//chrome/android/modules/foo/internal/entrypoints.cc`. This method is invoked | 
 | on all Chrome build variants, including Monochrome (unlike base module JNI). | 
 |  | 
 | ```c++ | 
 | #include "third_party/jni_zero/jni_zero_helper.h" | 
 | #include "base/android/jni_utils.h" | 
 | #include "chrome/android/modules/foo/internal/jni_registration.h" | 
 |  | 
 | extern "C" { | 
 | // This JNI registration method is found and called by module framework code. | 
 | JNI_ZERO_BOUNDARY_EXPORT bool JNI_OnLoad_foo(JNIEnv* env) { | 
 |   if (!foo::RegisterNatives(env)) { | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 | }  // extern "C" | 
 | ``` | 
 |  | 
 | Next, include the module entrypoint and related pieces in the build config at | 
 | `//chrome/android/modules/foo/internal/BUILD.gn`: | 
 |  | 
 | ```gn | 
 | import("//build/config/android/rules.gni") | 
 | import("//chrome/android/modules/buildflags.gni") | 
 | ... | 
 |  | 
 | # Put the JNI entrypoint in a component, so that the component build has a | 
 | # library to include in the foo module. This makes things feel consistent with | 
 | # a release build. | 
 | component("foo") { | 
 |   sources = [ | 
 |     "entrypoints.cc", | 
 |   ] | 
 |   deps = [ | 
 |     ":jni_registration", | 
 |     "//base", | 
 |     "//chrome/browser/foo/internal:native", | 
 |   ] | 
 |  | 
 |   # Instruct the compiler to flag exported entrypoint function as belonging in | 
 |   # foo's library. The linker will use this information when creating the | 
 |   # native libraries. The partition name must be <feature>_partition. | 
 |   if (use_native_partitions) { | 
 |     cflags = [ "-fsymbol-partition=foo_partition" ] | 
 |   } | 
 | } | 
 |  | 
 | # Generate JNI registration for the methods called by the Java side. Note the | 
 | # no_transitive_deps argument, which ensures that JNI is generated for only the | 
 | # specified Java target, and not all its transitive deps (which could include | 
 | # the base module). | 
 | generate_jni_registration("jni_registration") { | 
 |   targets = [ "//chrome/browser/foo/internal:java" ] | 
 |   namespace = "foo" | 
 |   no_transitive_deps = true | 
 |   manual_jni_registration = true | 
 | } | 
 |  | 
 | # This group is a convenience alias representing the module's native code, | 
 | # allowing it to be named "native" for clarity in module descriptors. | 
 | group("native") { | 
 |   deps = [ | 
 |     ":foo", | 
 |   ] | 
 | } | 
 | ``` | 
 |  | 
 | Now, over to the implementation of the module. These are the parts that | 
 | shouldn't know or care whether they're living in a module or not. | 
 |  | 
 | Add a stub implementation in | 
 | `//chrome/browser/foo/internal/android/foo_impl.cc`: | 
 |  | 
 | ```c++ | 
 | #include "base/logging.h" | 
 | #include "chrome/browser/foo/internal/jni_headers/FooImpl_jni.h" | 
 |  | 
 | static int JNI_FooImpl_Execute(JNIEnv* env) { | 
 |   LOG(INFO) << "Running foo feature code!"; | 
 |   return 123; | 
 | } | 
 | ``` | 
 |  | 
 | And, the associated build config in | 
 | `//chrome/browser/foo/internal/BUILD.gn`: | 
 |  | 
 | ```gn | 
 | import("//build/config/android/rules.gni") | 
 |  | 
 | ... | 
 |  | 
 | source_set("native") { | 
 |   sources = [ | 
 |     "android/foo_impl.cc", | 
 |   ] | 
 |  | 
 |   deps = [ | 
 |     ":jni_headers", | 
 |     "//base", | 
 |   ] | 
 | } | 
 |  | 
 | generate_jni("jni_headers") { | 
 |   sources = [ | 
 |     "android/java/src/org/chromium/chrome/browser/foo/FooImpl.java", | 
 |   ] | 
 | } | 
 | ``` | 
 |  | 
 | With a declaration of the native method on the Java side: | 
 |  | 
 | ```java | 
 | public class FooImpl implements Foo { | 
 |     ... | 
 |  | 
 |     @NativeMethods("foo") | 
 |     interface Natives { | 
 |         int execute(); | 
 |     } | 
 | } | 
 | ``` | 
 |  | 
 | Finally, augment the module descriptor in | 
 | `//chrome/android/modules/foo/foo_module.gni` with the native dependencies: | 
 |  | 
 | ```gn | 
 | foo_module_desc = { | 
 |   ... | 
 |   native_deps = [ | 
 |     "//chrome/android/modules/foo/internal:native", | 
 |     "//chrome/browser/foo/internal:native", | 
 |   ] | 
 |   load_native_on_get_impl = true | 
 | } | 
 | ``` | 
 |  | 
 | If `load_native_on_get_impl` is set to `true` then Chrome automatically loads | 
 | Foo DFM's native libraries and PAK file resources when `FooModule.getImpl()` is | 
 | called for the first time. The loading requires Chrome's main native libraries | 
 | to be loaded. If you wish to call `FooModule.getImpl()` earlier than that, then | 
 | you'd need to set `load_native_on_get_impl` to `false`, and manage native | 
 | libraries / resources loading yourself (potentially, on start-up and on install, | 
 | or on use). | 
 |  | 
 | #### Calling feature module native code from base the module | 
 |  | 
 | If planning to use direct native-native calls into DFM code, then the module | 
 | should have a purely virtual interface available. The main module can obtain a | 
 | pointer to a DFM-created object or factory (implemented by the feature), and | 
 | call its virtual methods. | 
 |  | 
 | Ideally, the interface to the feature will avoid feature-specific types. If a | 
 | feature defines complex data types, and uses them in its own interface, then it's | 
 | likely the main library will utilize the code backing these types. That code, | 
 | and anything it references, will in turn be pulled back into the main library, | 
 | negating the intent to house code in the DFM. | 
 |  | 
 | Therefore, designing the feature interface to use C types, C++ standard types, | 
 | or classes that aren't expected to move out of Chrome's main library is ideal. | 
 | If feature-specific classes are needed, they simply need to avoid referencing | 
 | feature library internals. | 
 |  | 
 | ### Adding Android resources | 
 |  | 
 | In this section we will add the required build targets to add Android resources | 
 | to the Foo DFM. | 
 |  | 
 | First, add a resources target to | 
 | `//chrome/browser/foo/internal/BUILD.gn` and add it as a dependency on | 
 | Foo's `java` target in the same file: | 
 |  | 
 | ```gn | 
 | ... | 
 | android_resources("java_resources") { | 
 |   # Define like ordinary Android resources target. | 
 |   ... | 
 |   custom_package = "org.chromium.chrome.browser.foo" | 
 | } | 
 | ... | 
 | android_library("java") { | 
 |   ... | 
 |   deps = [ | 
 |     ":java_resources", | 
 |   ] | 
 | } | 
 | ``` | 
 |  | 
 | To add strings follow steps | 
 | [here](http://dev.chromium.org/developers/design-documents/ui-localization) to | 
 | add new Java GRD file. Then create | 
 | `//chrome/browser/foo/internal/android/resources/strings/android_foo_strings.grd` as | 
 | follows: | 
 |  | 
 | ```xml | 
 | <?xml version="1.0" encoding="UTF-8"?> | 
 | <grit current_release="1" latest_public_release="0"> | 
 |   <outputs> | 
 |     <output | 
 |         filename="values-am/android_foo_strings.xml" | 
 |         lang="am" | 
 |         type="android" /> | 
 |     <!-- List output file for all other supported languages. See | 
 |          //chrome/browser/ui/android/strings/android_chrome_strings.grd for the | 
 |          full list. --> | 
 |     ... | 
 |   </outputs> | 
 |   <translations> | 
 |     <file lang="am" path="vr_translations/android_foo_strings_am.xtb" /> | 
 |     <!-- Here, too, list XTB files for all other supported languages. --> | 
 |     ... | 
 |   </translations> | 
 |   <release seq="1"> | 
 |     <messages fallback_to_english="true"> | 
 |       <message name="IDS_BAR_IMPL_TEXT" desc="Magical string."> | 
 |         impl | 
 |       </message> | 
 |     </messages> | 
 |   </release> | 
 | </grit> | 
 | ``` | 
 |  | 
 | Then, create a new GRD target and add it as a dependency on `java_resources` in | 
 | `//chrome/browser/foo/internal/BUILD.gn`: | 
 |  | 
 | ```gn | 
 | ... | 
 | java_strings_grd("java_strings_grd") { | 
 |   defines = chrome_grit_defines | 
 |   grd_file = "android/resources/strings/android_foo_strings.grd" | 
 | } | 
 | ... | 
 | android_resources("java_resources") { | 
 |   ... | 
 |   deps = [":java_strings_grd"] | 
 |   custom_package = "org.chromium.chrome.browser.foo" | 
 | } | 
 | ... | 
 | ``` | 
 |  | 
 | You can then access Foo's resources using the | 
 | `org.chromium.chrome.browser.foo.R` class. To do this change | 
 | `//chrome/browser/foo/internal/android/java/src/org/chromium/chrome/browser/foo/FooImpl.java` | 
 | to: | 
 |  | 
 | ```java | 
 | package org.chromium.chrome.browser.foo; | 
 |  | 
 | import org.chromium.base.ContextUtils; | 
 | import org.chromium.base.Log; | 
 | import org.chromium.chrome.browser.foo.R; | 
 |  | 
 | public class FooImpl implements Foo { | 
 |     @Override | 
 |     public void bar() { | 
 |         Log.i("FOO", ContextUtils.getApplicationContext().getString( | 
 |                 R.string.bar_impl_text)); | 
 |     } | 
 | } | 
 | ``` | 
 |  | 
 | ### Adding non-string native resources | 
 |  | 
 | This section describes how to add non-string native resources to Foo DFM. | 
 | Key ideas: | 
 |  | 
 | * The compiled resource file shipped with the DFM is `foo_resourcess.pak`. | 
 | * At run time, native resources need to be loaded before use. Also, DFM native | 
 |   resources can only be used from the Browser process. | 
 |  | 
 | #### Creating PAK file | 
 |  | 
 | Two ways to create `foo_resourcess.pak` (using GRIT) are: | 
 |  | 
 | 1. (Preferred) Use `foo_resourcess.grd` to refer to individual files (e.g., | 
 |   images, HTML, JS, or CSS) and assigns resource text IDs. `foo_resourcess.pak` | 
 |   must have an entry in `/tools/gritsettings/resource_ids.spec`. | 
 | 1. Combine existing .pak files via `repack` rules in GN build files. This is | 
 |   done by the DevUI DFM, which aggregates resources from many DevUI pages. | 
 |  | 
 | #### Loading PAK file | 
 |  | 
 | At runtime, `foo_resources.pak` needs to be loaded (memory-mapped) before any of | 
 | its resource gets used. Alternatives to do this are: | 
 |  | 
 | 1. (Simplest) Specify native resources (with native libraries if any exist) to | 
 |   be automatically loaded on first call to `FooModule.getImpl()`. This behavior | 
 |   is specified via `load_native_on_get_impl = true` in `foo_module_desc`. | 
 | 1. In Java code, call `FooModule.ensureNativeLoaded()`. | 
 | 1. In C++ code, use JNI to call `FooModule.ensureNativeLoaded()`. The code to do | 
 |   this can be placed in a helper class, which can also have JNI calls to | 
 |   `FooModule.isInstalled()` and `FooModule.installModule()`. | 
 |  | 
 | #### Cautionary notes | 
 |  | 
 | Compiling `foo_resources.pak` auto-generates `foo_resources.h`, which defines | 
 | textual resource IDs, e.g., `IDR_FOO_HTML`. C++ code then uses these IDs to get | 
 | resource bytes. Unfortunately, this behavior is fragile: If `IDR_FOO_HTML` is | 
 | accessed before the Foo DFM is (a) installed, or (b) loaded, then runtime error | 
 | ensues! Some mitigation strategies are as follows: | 
 |  | 
 | * (Ideal) Access Foo DFM's native resources only from code in Foo DFM's native | 
 |   libraries. So by the time that `IDR_FOO_HTML` is accessed, everything is | 
 |   already in place! This isn't always possible; henceforth we assume that | 
 |   `IDR_FOO_HTML` is accessed by code in the base DFM. | 
 | * Before accessing IDR_FOO_HTML, ensure Foo DFM is installed and loaded. The | 
 |   latter can use `FooModule.ensureNativeLoaded()` (needs to be called from | 
 |   Browser thread). | 
 | * Use inclusion of `foo_resources.h` to restrict availability of `IDR_FOO_HTML`. | 
 |   Only C++ files dedicated to "DFM-gated code" (code that runs only when its DFM | 
 |   is installed and loaded) should include `foo_resources.h`. | 
 |  | 
 | #### Associating native resources with DFM | 
 |  | 
 | Here are the main GN changes to specify PAK files and default loading behavior | 
 | for a DFM's native resources: | 
 |  | 
 | ```gn | 
 | foo_module_desc = { | 
 |   ... | 
 |   paks = [ "$root_gen_dir/chrome/browser/foo/internal/foo_resourcess.pak" ] | 
 |   pak_deps = [ "//chrome/browser/foo/internal:foo_paks" ] | 
 |   load_native_on_get_impl = true | 
 | } | 
 | ``` | 
 |  | 
 | Note that `load_native_on_get_impl` specifies both native libraries and native | 
 | resources. | 
 |  | 
 |  | 
 | ### Module install | 
 |  | 
 | So far, we have installed the Foo DFM as a true split (`-m foo` option on the | 
 | install script). In production, however, we have to explicitly install the Foo | 
 | DFM for users to get it. There are three install options: _on-demand_, | 
 | _deferred_ and _conditional_. | 
 |  | 
 | #### On-demand install | 
 |  | 
 | On-demand requesting a module will try to download and install the | 
 | module as soon as possible regardless of whether the user is on a metered | 
 | connection or whether they have turned updates off in the Play Store app. | 
 |  | 
 | You can use the autogenerated module class to on-demand install the module like | 
 | so: | 
 |  | 
 | ```java | 
 | FooModule.install((success) -> { | 
 |     if (success) { | 
 |         FooModule.getImpl().bar(); | 
 |     } | 
 | }); | 
 | ``` | 
 |  | 
 | **Optionally**, you can show UI telling the user about the install flow. For | 
 | this, add a function like the one below. Note, it is possible | 
 | to only show either one of the  install, failure and success UI or any | 
 | combination of the three. | 
 |  | 
 | ```java | 
 | public static void installModuleWithUi( | 
 |         Tab tab, OnModuleInstallFinishedListener onFinishedListener) { | 
 |     ModuleInstallUi ui = | 
 |             new ModuleInstallUi( | 
 |                     tab, | 
 |                     R.string.foo_module_title, | 
 |                     new ModuleInstallUi.FailureUiListener() { | 
 |                         @Override | 
 |                         public void onFailureUiResponse(retry) { | 
 |                             if (retry) { | 
 |                                 installModuleWithUi(tab, onFinishedListener); | 
 |                             } else { | 
 |                                 onFinishedListener.onFinished(false); | 
 |                             } | 
 |                         } | 
 |                     }); | 
 |     // At the time of writing, shows toast informing user about install start. | 
 |     ui.showInstallStartUi(); | 
 |     FooModule.install( | 
 |             (success) -> { | 
 |                 if (!success) { | 
 |                     // At the time of writing, shows infobar allowing user | 
 |                     // to retry install. | 
 |                     ui.showInstallFailureUi(); | 
 |                     return; | 
 |                 } | 
 |                 // At the time of writing, shows toast informing user about | 
 |                 // install success. | 
 |                 ui.showInstallSuccessUi(); | 
 |                 onFinishedListener.onFinished(true); | 
 |             }); | 
 | } | 
 | ``` | 
 |  | 
 | To test on-demand install, "fake-install" the DFM. It's fake because | 
 | the DFM is not installed as a true split. Instead it will be emulated by play | 
 | core's `--local-testing` [mode][play-core-local-testing]. | 
 | Fake-install and launch Chrome with the following command: | 
 |  | 
 | ```shell | 
 | $ $OUTDIR/bin/monochrome_public_bundle install -f foo | 
 | $ $OUTDIR/bin/monochrome_public_bundle launch | 
 | ``` | 
 |  | 
 | When running the install code, the Foo DFM module will be emulated. | 
 | This will be the case in production right after installing the module. Emulation | 
 | will last until Play Store has a chance to install your module as a true split. | 
 | This usually takes about a day. After it has been installed, it will be updated | 
 | atomically alongside Chrome. Always check that it is installed and available | 
 | before invoking code within the DFM. | 
 |  | 
 | *** note | 
 | **Warning:** There are subtle differences between emulating a module and | 
 | installing it as a true split. We therefore recommend that you always test both | 
 | install methods. | 
 | *** | 
 |  | 
 | *** note | 
 | To simplify development, the DevUI DFM (dev_ui) is installed by default, i.e., | 
 | `-m dev_ui` is implied by default. This is overridden by: | 
 | * `--no-module dev_ui`, to test error from missing DevUI, | 
 | * `-f dev_ui`, for fake module install. | 
 | *** | 
 |  | 
 | #### Deferred install | 
 |  | 
 | Deferred install means that the DFM is installed in the background when the | 
 | device is on an unmetered connection and charging. The DFM will only be | 
 | available after Chrome restarts. When deferred installing a module it will | 
 | not be faked installed. | 
 |  | 
 | To defer install Foo do the following: | 
 |  | 
 | ```java | 
 | FooModule.installDeferred(); | 
 | ``` | 
 |  | 
 | #### Conditional install | 
 |  | 
 | Conditional install means the DFM will be installed automatically upon first | 
 | installing or updating Chrome if the device supports a particular feature. | 
 | Conditional install is configured in the module's manifest. To install your | 
 | module on all Daydream-ready devices for instance, your | 
 | `//chrome/android/modules/foo/internal/java/AndroidManifest.xml` should look | 
 | like this: | 
 |  | 
 | ```xml | 
 | <?xml version="1.0" encoding="utf-8"?> | 
 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 
 |     xmlns:dist="http://schemas.android.com/apk/distribution" | 
 |     featureSplit="foo"> | 
 |  | 
 |     <dist:module | 
 |       dist:instant="false" | 
 |       dist:title="@string/foo_module_title"> | 
 |       <dist:fusing dist:include="true" /> | 
 |       <dist:delivery> | 
 |         <dist:install-time> | 
 |           <dist:conditions> | 
 |             <dist:device-feature | 
 |               dist:name="android.hardware.vr.high_performance" /> | 
 |           </dist:conditions> | 
 |         </dist:install-time> | 
 |         <!-- Allows on-demand or deferred install on non-Daydream-ready | 
 |              devices. --> | 
 |         <dist:on-demand /> | 
 |       </dist:delivery> | 
 |     </dist:module> | 
 |  | 
 |     <application /> | 
 | </manifest> | 
 | ``` | 
 |  | 
 | You can also specify no conditions to have your module always installed. | 
 | You might want to do this in order to delay the performance implications | 
 | of loading your module until its first use (true only on Android O+ where | 
 | [android:isolatedSplits](https://developer.android.com/reference/android/R.attr#isolatedSplits) | 
 | is supported. See [go/isolated-splits-dev-guide](http://go/isolated-splits-dev-guide) | 
 | (googlers only). | 
 |  | 
 | ### chrome_public_apk and Integration Tests | 
 |  | 
 | To make the Foo feature available in the non-bundle `chrome_public_apk` | 
 | target, add the `java` target to the template in | 
 | `//chrome/android/chrome_public_apk_tmpl.gni` like so: | 
 |  | 
 | ```gn | 
 |   # Add to where "chrome_all_java" is added: | 
 |   if (!_is_bundle) { | 
 |     deps += [ "//chrome/browser/foo/internal:java" ] | 
 |   } | 
 | } | 
 | ``` | 
 |  | 
 | You may also have to add `java` as a dependency of | 
 | `//chrome/android/javatests/chrome_test_java_org.chromium.chrome.browser.foo` | 
 | if you want to call into Foo from test code. | 
 |  | 
 | [play-core-local-testing]: https://developer.android.com/guide/playcore/feature-delivery/on-demand#local-testing |