# Trusted Web Activity

> How a Trusted Web Activity ships a PWA through Google Play, how Digital Asset Links decides whether the URL bar shows, and how it differs from a WebAPK.

A Trusted Web Activity (TWA) is an Android activity that shows a web origin full-screen in the user's Chrome, with no URL bar, after Digital Asset Links has proved that the APK's signing key and the website belong to the same party. It is the route by which a PWA is listed on Google Play; Chrome 72 introduced it on Android, and the protocol is open to other browsers, though Chrome's documentation records support only for Chrome itself.

## How it works

The Android app contains no web content. Its launcher activity (`com.google.androidbrowserhelper.trusted.LauncherActivity` from the `android-browser-helper` library) binds to the user's default browser through the Custom Tabs service, asks it to open the PWA's `start_url` in trusted mode, and the browser renders the site in its own process with the user's cookies, service workers, and storage.

Trust is established once per launch:

1. The website publishes `/.well-known/assetlinks.json` listing the app's package name and the SHA-256 fingerprint of its signing certificate under the relation `delegate_permission/common.handle_all_urls`.
2. The APK declares the same origin in its `asset_statements` string resource.
3. At launch the browser fetches the assetlinks file (served as `application/json`, reachable without redirects) and compares fingerprints. A match hides the URL bar; a mismatch, a missing file, or a redirect falls back to a normal Custom Tab with the URL bar visible, which is the designed degradation rather than an error.

Because Play signs the uploaded app with its own key under Play App Signing, the fingerprint in `assetlinks.json` has to be the Play-managed signing key, not the local upload key; this is the single most common cause of the URL bar appearing in production.

The alternative Android install paths differ in who builds the artefact and what it can reach:

| | TWA | WebAPK (Chrome install) | Home Screen shortcut |
|---|---|---|---|
| Distributed by | Google Play (also Galaxy Store, F-Droid) | Chrome at install time | Any browser |
| You build an Android project | Yes (Bubblewrap or Android Studio) | No | No |
| Digital Asset Links | Required | Not used | Not used |
| Play Billing | Yes, via the Digital Goods API | No | No |
| Updates | Web content: instantly; wrapper: a Play release | Manifest re-check by Chrome | None |
| Runs in | The user's default browser, if it supports TWA | Chrome | The installing browser |

Play Billing is reached from the web page through the Digital Goods API (`window.getDigitalGoodsService('https://play.google.com/billing')`) together with Payment Request, available only inside a verified TWA; Play policy requires it for digital goods sold in the app.

## Examples

The three artefacts below are the whole integration: the assetlinks file on the site, the wrapper built from the manifest, and the page-side check.

### Publishing the Digital Asset Links statement

The file lives at the origin root; one array entry per package name and per signing key (debug and release keys need separate fingerprints).

```json
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.app",
    "sha256_cert_fingerprints": [
      "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
    ]
  }
}]
```

The Play Console shows the correct fingerprint under Release › Setup › App signing › "App signing key certificate"; a statement built from `keytool` against the upload key passes locally and fails on Play-distributed builds.

### Generating and building the wrapper with Bubblewrap

Bubblewrap reads the web app manifest and writes an Android project with the package name, icons, colours, and `asset_statements` filled in.

```sh
npm i -g @bubblewrap/cli
bubblewrap init --manifest https://example.com/manifest.webmanifest
bubblewrap build          # produces app-release-signed.apk and app-release-bundle.aab
bubblewrap validate --url https://example.com/   # Lighthouse-based PWA check
```

The generated project pins `android-browser-helper`; `bubblewrap update` regenerates it from a changed manifest. Shipping the `.aab` to Play means the fingerprint in `assetlinks.json` must be Play's signing key as described above.

### Detecting that the page is running inside the TWA

The browser sets the document referrer to the Android package that launched it, which is the signal to turn on Play-only features and to hide web install prompts.

```js
const fromTwa = document.referrer.startsWith('android-app://com.example.app/');

if (fromTwa && 'getDigitalGoodsService' in window) {
  enablePlayBilling();                      // Digital Goods API is available here
} else {
  enableWebCheckout();                      // regular web payments everywhere else
}
installButton.hidden = fromTwa || matchMedia('(display-mode: standalone)').matches;
```

The referrer is present only on the first navigation of the session, so store the result (for example in `sessionStorage`) before the first in-app navigation clears it; `display-mode: standalone` is also `true` inside a TWA, but it cannot distinguish a TWA from a WebAPK.

:::observed
When the assetlinks fingerprint does not match the APK's signing key, the app opens with Chrome's Custom Tab toolbar and URL bar visible instead of full-screen, with no error shown to the user; the Chrome TWA overview documents this fallback. On Android 12 and later, `adb shell pm get-app-links com.example.app` prints the declared domains with their verification state (`verified`, `none`, or a legacy code, per the Android App Links verification guide); a `none` state with a correct file on the server means the device fetched the file before it was deployed, and `adb shell pm verify-app-links --re-verify com.example.app` forces a re-fetch.
:::

## See also

- [Trusted Web Activities overview](https://developer.chrome.com/docs/android/trusted-web-activity/overview) (developer.chrome.com)
- [Verify Android App Links](https://developer.android.com/training/app-links/verify-android-applinks) (developer.android.com)
- [Bubblewrap](https://github.com/GoogleChromeLabs/bubblewrap) (github.com)
- [Trusted Web Activity browser support](/compatibility/twa/)
- [WebAPK](/reference/installation/webapk/)
- [getInstalledRelatedApps()](/reference/installation/get-installed-related-apps/)
- [Distribution and store policy](/ecosystem/distribution/)