These instructions explain how to build and run Cobalt for the AOSP platform.

Before following these instructions, make sure you have set up your workstation and source checkout as described in [Set up your environment - Linux](https://developers.google.com/youtube/cobalt/docs/development/setup-linux).

## Prerequisites

1. Follow all steps in [Set up your environment - Linux](https://developers.google.com/youtube/cobalt/docs/development/setup-linux) to install basic system dependencies, `depot_tools`, clone the Cobalt repository, and run `build/install-build-deps.sh`.

2. Ensure your root `.gclient` file includes `android` in `target_os`:

       target_os = [ 'linux', 'android' ]

   Then synchronize dependencies from your top-level repository:

       cd ~/cobalt/src
       gclient sync

## Running in Evergreen Mode

Because Evergreen support is required for certification, partners deploy official Google prebuilt `.crx` packages (available on [GitHub Releases](https://github.com/youtube/cobalt/releases)) into the Slot 0 directory structure, compile the `cobalt_loader` APK, and deploy to target hardware.

### Deploying Official Google Prebuilt CRX Packages (Primary Flow)

1. Ensure environment variables are set and initialize an AOSP build directory:

       export PATH="$HOME/depot_tools:$PATH"

       # For 32-bit ARM AOSP targets
       cobalt/build/gn.py -p evergreen-arm-softfp-aosp -c qa --no-rbe

       # For 64-bit ARM AOSP targets
       cobalt/build/gn.py -p evergreen-arm64-aosp -c qa --no-rbe

2. Build the application loader APK:

       autoninja -C out/evergreen-arm-softfp-aosp_qa cobalt_loader

   This generates the application loader APK at `out/evergreen-arm-softfp-aosp_qa/apks/cobalt.apk`.
3. Download the official prebuilt CRX file from [GitHub Releases](https://github.com/youtube/cobalt/releases):

       export LOCAL_CRX_DIR=/tmp/cobalt_dl
       rm -rf $LOCAL_CRX_DIR && mkdir -p $LOCAL_CRX_DIR

       # Download the prebuilt CRX corresponding to your target architecture (e.g. arm-softfp or arm64)
       COBALT_CRX_URL="https://github.com/youtube/cobalt/releases/download/<version>/cobalt_evergreen_<version>_arm-softfp_<config>.crx"
       wget $COBALT_CRX_URL -O $LOCAL_CRX_DIR/cobalt_prebuilt.crx

4. Unpack and inject the official prebuilt CRX into the compiled APK:

       unzip $LOCAL_CRX_DIR/cobalt_prebuilt.crx -d $LOCAL_CRX_DIR/cobalt_prebuilt

       # Inject prebuilt Slot 0 Core library and manifest into the APK assets
       cd $LOCAL_CRX_DIR/cobalt_prebuilt
       mkdir -p assets/app/cobalt/lib assets/app/cobalt/content
       cp -f manifest.json assets/app/cobalt/
       cp -rf lib/* assets/app/cobalt/lib/
       cp -rf content/* assets/app/cobalt/content/

       # zip -u does not recurse into subdirectories; use a file list so that
       # content/ssl/certs/* and content/fonts/* are included
       find assets -type f | zip -u $OLDPWD/out/evergreen-arm-softfp-aosp_qa/apks/cobalt.apk -@

       # zip -u invalidates the APK v2 signature; re-align and re-sign with the bundled debug keystore
       cd $OLDPWD
       APK="out/evergreen-arm-softfp-aosp_qa/apks/cobalt.apk"
       BT=$(ls -d third_party/android_sdk/public/build-tools/* | tail -1)
       $BT/zipalign -f -p 4 $APK $APK.aligned && mv $APK.aligned $APK
       $BT/apksigner sign \
         --ks build/android/chromium-debug.keystore \
         --ks-key-alias chromiumdebugkey --ks-pass pass:chromium \
         --in $APK --out $APK.signed && mv $APK.signed $APK
       $BT/apksigner verify --verbose $APK

5. Deploy and launch on an AOSP device or emulator:

   Ensure your device is connected via ADB (`adb devices` or `adb connect <device_ip>:5555`).

   Install the compiled APK:

       adb install -r out/evergreen-arm-softfp-aosp_qa/apks/cobalt.apk

   Launch the application using `adb` (Package: `dev.cobalt.coat`, Activity: `dev.cobalt.app.MainActivity`):

       adb shell am start dev.cobalt.coat/dev.cobalt.app.MainActivity

   Pass runtime flags or custom URL using `--esa commandLineArgs`:

       adb shell am start --esa commandLineArgs 'url=https://www.youtube.com/tv' dev.cobalt.coat/dev.cobalt.app.MainActivity

   To force-stop any running instance before relaunching:

       adb shell am force-stop dev.cobalt.coat

*** ** * ** ***

### Compiling Custom Cobalt Core from Source (For Core Engine Debugging Only)

> \[!CAUTION\]
> SoC and OEM partners are required to use official Google Prebuilt CRX packages for testing and certification. Compiling Cobalt Core (`libcobalt.so`) from source is intended only for core developers debugging internal engine changes.

1. Configure the build directory for the target AOSP platform using `cobalt/build/gn.py`.

   Use the `-c` flag to specify a `build_type` (`debug`, `devel`, `qa`, or `gold`).

       cobalt/build/gn.py -p evergreen-arm-softfp-aosp -c devel --no-rbe

2. Compile the `cobalt_loader` target using `autoninja`:

       autoninja -C out/evergreen-arm-softfp-aosp_devel cobalt_loader

   This generates the application loader APK at `out/evergreen-arm-softfp-aosp_devel/apks/cobalt.apk`.
3. Deploy and launch on an AOSP device or emulator:

   Ensure your device is connected via ADB (`adb devices` or `adb connect <device_ip>:5555`).

   Install the compiled APK:

       adb install -r out/evergreen-arm-softfp-aosp_devel/apks/cobalt.apk

   Launch the application using `adb` (Package: `dev.cobalt.coat`, Activity: `dev.cobalt.app.MainActivity`):

       adb shell am start dev.cobalt.coat/dev.cobalt.app.MainActivity

   Pass runtime flags using `--esa commandLineArgs`:

       adb shell am start --esa commandLineArgs 'url=https://www.youtube.com/tv' dev.cobalt.coat/dev.cobalt.app.MainActivity

   To force-stop any running instance before relaunching:

       adb shell am force-stop dev.cobalt.coat

## Running Tests

The No Platform Left Behind (NPLB) test suite verifies Starboard implementation on AOSP targets.

1. Compile the NPLB test suite:

       cobalt/build/gn.py -p evergreen-arm-softfp-aosp -c devel --no-rbe
       autoninja -C out/evergreen-arm-softfp-aosp_devel nplb_loader

   This generates the test APK at
   `out/evergreen-arm-softfp-aosp_devel/nplb_loader_apk/nplb_loader-debug.apk` and the
   `out/evergreen-arm-softfp-aosp_devel/bin/run_nplb_loader` wrapper that drives it.

### Option A: Using the Test Runner Wrapper (Build Host Connected to Device)

1. Run NPLB on the target device. The wrapper installs the APK, pushes the
   runtime dependencies and collects the results:

       out/evergreen-arm-softfp-aosp_devel/bin/run_nplb_loader

2. Pass standard Google Test filtering arguments:

       out/evergreen-arm-softfp-aosp_devel/bin/run_nplb_loader --gtest-filter='*Memory*'

3. Any other argument is forwarded to NPLB:

       out/evergreen-arm-softfp-aosp_devel/bin/run_nplb_loader --gtest_shuffle

Every test target is built and run the same way, so an upstream test suite only
needs its own `<suite>_loader` target:

    autoninja -C out/evergreen-arm-softfp-aosp_devel zlib_unittests_loader
    out/evergreen-arm-softfp-aosp_devel/bin/run_zlib_unittests_loader

### Option B: Standalone APK Execution via ADB (Separate Build Server and Test Host)

When compiling on a remote Linux build server and testing on a separate local
machine connected to the AOSP device via ADB, package the APK and its runtime
dependencies the way CI does, copy the archive to the test host and run the
tests through the APK's instrumentation with `adb`:

1. The build machine must create `nplb_loader_deps.tar.gz`:

       cobalt/build/archive_test_artifacts.py --source-dir . \
         --out-dir out/evergreen-arm-softfp-aosp_devel/ --destination-dir . \
         --targets starboard/nplb:nplb_loader \
         --compression gz --archive-per-target --use-android-deps-path --flatten-deps

2. On the machine with the device, unpack it, install the APK, let it write
   its output to shared storage and push the test data:

       mkdir nplb && tar xzf nplb_loader_deps.tar.gz -C nplb
       adb install -r nplb/nplb_loader_apk/nplb_loader-debug.apk
       adb shell appops set dev.cobalt.coat MANAGE_EXTERNAL_STORAGE allow
       adb shell rm -rf /sdcard/chromium_tests_root/
       adb push nplb/test/ /sdcard/chromium_tests_root/test/

3. Run the tests. `-w` waits until the run ends. The test output goes to the
   given file, which is appended to, so remove it first. The default timeout
   is 60 seconds, too short for a full run:

       TIMEOUT_NS=$((60 * 60 * 1000000000))  # 1 hour
       adb shell rm -f /sdcard/nplb.txt
       adb shell am instrument -w \
         -e org.chromium.native_test.NativeTest.GtestFilter "'*Memory*'" \
         -e org.chromium.native_test.NativeTestInstrumentationTestRunner.StdoutFile /sdcard/nplb.txt \
         -e org.chromium.native_test.NativeTestInstrumentationTestRunner.ShardNanoTimeout $TIMEOUT_NS \
         dev.cobalt.coat/dev.cobalt.testing.CobaltInstrumentationTestRunner

   For a full run, exclude the tests listed in
   `cobalt/testing/filters/evergreen-arm-softfp-aosp/nplb_loader_filter.json`
   with a negative filter, e.g. `"'-*SbPlayer*:*MultiplePlayer*'"`.
4. Read the results:

       adb shell cat /sdcard/nplb.txt

   `am instrument` reports `INSTRUMENTATION_CODE: -1` whatever the outcome.
   A finished run ends with gtest's `[ PASSED ]` or `[ FAILED ]` summary;
   if it is missing, the tests crashed or timed out, and logcat has the
   details.

## Debugging

To monitor log output, watch logcat with a filter for Starboard and Cobalt messages:

    adb logcat -s "starboard:*" "Cobalt:*"

## Clean up or reset the environment

To clean build artifacts:

    gn clean out/evergreen-arm-softfp-aosp_devel