Skip to content
Tolinku
Tolinku
Sign In Start Free
Android Development · · 8 min read

Testing Android App Links: Tools and Techniques

By Tolinku Staff
|
Tolinku app links dashboard screenshot for android blog posts

App Links verification is one of those things that can appear to work perfectly in your development environment and then fail silently on production devices. The intent filter looks right, the assetlinks.json file seems fine, and yet users are still seeing the disambiguation dialog.

The reason is that testing App Links properly requires a specific set of tools and a methodical approach. This guide covers every layer of the testing workflow: validating the server-side file, checking verification status on a device, firing test intents with ADB, using Android Studio's built-in assistant, and diagnosing the failures that show up most often.

Before testing anything on a device, confirm that the file your server is serving is valid. A misconfigured or inaccessible file is the most common reason App Links fail, and catching it here saves time.

The Google Digital Asset Links verification API provides a public endpoint for checking your file:

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://yourdomain.com&relation=delegate_permission/common.handle_all_urls

Replace yourdomain.com with your actual domain and open the URL in a browser. A valid response looks like this:

{
  "statements": [
    {
      "source": { "web": { "site": "https://yourdomain.com" } },
      "relation": "delegate_permission/common.handle_all_urls",
      "target": {
        "androidApp": {
          "packageName": "com.example.myapp",
          "certificate": { "sha256Fingerprint": "AA:BB:CC:..." }
        }
      }
    }
  ],
  "complete": true
}

If "complete": true is present, the file is valid and Google's servers can read it. If you get an error or an empty statements array, the file has a problem: the URL is wrong, the file is not accessible, the JSON is malformed, or the content type is incorrect.

Check the HTTP response directly

Use curl to inspect exactly what your server returns, including headers:

curl -I https://yourdomain.com/.well-known/assetlinks.json

Check that:

  • The status code is 200 OK (not a redirect, not a 404)
  • The Content-Type header includes application/json
  • There are no authentication challenges (401, 403)

Then fetch the content itself to confirm the JSON is valid:

curl https://yourdomain.com/.well-known/assetlinks.json | python3 -m json.tool

If python3 -m json.tool reports an error, the JSON has a syntax problem.

Layer 2: Check Verification Status on a Device

Once you confirm the file is valid, install your app on a test device and check whether Android accepted the verification.

This is the most direct way to see the verification status for your app:

adb shell pm get-app-links --package com.example.myapp

The output lists each domain your app has declared with autoVerify="true" and shows the current status for each. The possible statuses are:

Status Meaning
verified Verification passed. Your app is the default handler for this domain.
approved The user manually approved this domain in Settings.
denied The user manually removed this domain in Settings.
migrated Restored from a previous Android 11-style verification result.
restored Restored from device backup.
none No verification result yet, or verification has not been attempted.
rejected Verification was attempted and explicitly failed.

For production use, you want verified for every domain you care about. none can mean the device has not had a chance to run verification yet (try waiting a few minutes after installation), or that your server is unreachable from Google's infrastructure.

Triggering re-verification manually (Android 12+)

On Android 12 and later, you can trigger a fresh verification attempt without reinstalling the app:

adb shell pm verify-app-links --re-verify com.example.myapp

Wait a few seconds, then check the status again with pm get-app-links. This is much faster than the uninstall-reinstall cycle required on Android 11 and earlier.

On Android 11 and earlier, uninstall and reinstall the app to trigger a fresh verification attempt. There is no --re-verify option on those versions.

Layer 3: Fire Test Intents with ADB

After confirming verification status, test that your app actually handles URLs correctly by firing intents directly with ADB. This bypasses the disambiguation dialog and tells you definitively whether your intent filter is configured correctly.

Test a specific URL

adb shell am start -a android.intent.action.VIEW \
  -d "https://yourdomain.com/products/123" \
  com.example.myapp

Specifying the package name (com.example.myapp) at the end forces the intent to go to your app. If your activity launches, the intent filter is configured correctly and your activity can receive the URL.

To test without specifying the package name (simulating what happens when a user taps a link):

adb shell am start -a android.intent.action.VIEW \
  -d "https://yourdomain.com/products/123"

If your app is verified, it opens directly. If it is not verified, the disambiguation dialog appears or the browser opens. This simulates the real user experience.

Test that your app reads the URL correctly

Fire an intent and watch your app's logcat output to confirm the URL is being parsed and routed correctly:

adb logcat -s YourAppTag

Add log output in your activity's onCreate() and onNewIntent() methods during development:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val uri = intent?.data
    Log.d("DeepLink", "Received URI: $uri")
    Log.d("DeepLink", "Path: ${uri?.path}")
    Log.d("DeepLink", "Query: ${uri?.query}")
}

This lets you verify that the URL arrives correctly formatted and that your parsing logic extracts the right path segments and query parameters.

Testing from a specific activity state

When the app is already running in the background, incoming intents are delivered to onNewIntent() instead of onCreate(). Test this scenario explicitly:

  1. Launch your app normally and leave it running in the background.
  2. Fire the intent with ADB.
  3. Confirm that onNewIntent() is called and handles the URL.
override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    val uri = intent.data
    Log.d("DeepLink", "New intent received: $uri")
    // Handle the deep link
}

Many implementations handle onCreate() correctly but forget onNewIntent(), causing links to be ignored when the app is already open.

Android Studio includes an App Link Assistant that guides you through the intent filter and assetlinks.json setup and provides built-in testing tools. Access it from the menu: Tools > App Link Assistant.

Android Studio keystore wizard for generating signing keys

Source: Android Developer Documentation

The assistant has four tabs:

  1. Add URL intent filters: Helps you add intent filter XML to your manifest by entering URLs you want to handle.
  2. Add logic to handle the intent: Generates Kotlin/Java code for reading the URL from the intent.
  3. Associate website: Generates the assetlinks.json content and explains where to host it. If your site is already live, it can fetch the existing file and compare it against your app's signing certificate.
  4. Test on device: Provides a form where you enter a URL and fire it at the connected device. It reports whether the URL was handled by your app and whether it was handled as a verified App Link.

The App Link Assistant is a useful starting point for new implementations and for teams who prefer a visual interface over the command line. Its "Test on device" functionality is essentially a GUI wrapper around the adb shell am start command, but it also reports the verification status of the result.

Note that the assistant reads your app's debug signing certificate. If you distribute through Google Play App Signing, the certificate it generates will be your local certificate, not Google's re-signing certificate. You still need to get the correct fingerprint from the Google Play Console under Setup > App integrity for production App Links.

Layer 5: Testing on Real Devices

Emulators can run most App Links tests, but real devices are necessary for a complete picture:

Production signing certificates. Emulators typically run debug builds. To verify that the production assetlinks.json file works with your release certificate, you need a device with the release-signed APK installed. This can be a physical device or a Google Play internal testing track.

Google Play App Signing. If you use Play App Signing, the only way to test with the production certificate is to install an APK distributed through Play (internal testing, alpha, or beta channels). The signing certificate on a locally-built release APK will not match what Play installs.

Network conditions. Verification depends on Google's servers being able to reach your domain. Real devices on mobile networks help confirm that your server is accessible from the public internet, not just from your development network.

Android version spread. Verification behavior differs between Android 11 and Android 12+. If your users run a mix of OS versions, test on devices running both.

Installing a Play-distributed build for testing

To install from an internal testing track and test App Links:

  1. Publish a build to the internal testing track in the Play Console.
  2. Install it on a physical device via the Play Store (the tester must be added to the internal testing group).
  3. Run adb shell pm get-app-links --package com.example.myapp to confirm verification status.

Common Failures and How to Fix Them

Status shows "rejected"

The verification check ran but the assetlinks.json file content did not match your app. Most likely causes:

  • Wrong certificate fingerprint. Confirm you are using the fingerprint from the Google Play Console if you use Play App Signing, not your local keystore.
  • Wrong package name. The package_name in the JSON must match your applicationId in build.gradle, not the Java package.
  • JSON syntax error. Validate the file with the Digital Asset Links API.

Status shows "none" after installation

Verification has not completed or has not been attempted. Try:

adb shell pm verify-app-links --re-verify com.example.myapp

If it still shows "none" after a minute, the file may be unreachable from Google's servers. Test the URL from a different network or use the Digital Asset Links API to confirm accessibility.

App opens but routes to the wrong screen

The intent filter is working, but the URL parsing or routing logic in your activity has a bug. Check your onCreate() and onNewIntent() implementations. Add logging and fire test URLs with ADB to trace the path.

Works on emulator but not on device

Usually a signing certificate mismatch. The emulator might be running a debug build with your local certificate, while the device has a Play-distributed build signed by Google's key. Update the assetlinks.json to include the Play Console fingerprint.

For more detailed diagnosis steps and additional failure patterns, see the Tolinku Android troubleshooting guide and the complete Android App Links guide.

To be systematic, run through this sequence for any new App Links implementation:

  1. Validate assetlinks.json with the Google Digital Asset Links API.
  2. Check HTTP response headers with curl.
  3. Install the app and check adb shell pm get-app-links.
  4. If status is not verified, run pm verify-app-links --re-verify and check again.
  5. Fire a test URL with adb shell am start -d "https://..." (with package name) and confirm the app handles it.
  6. Fire the same URL without the package name and confirm it goes directly to the app without a dialog.
  7. Test with the app already in the background to confirm onNewIntent() works.
  8. Test with a release-signed build on a real device to confirm production certificates work.

See the Tolinku App Links developer documentation for configuration reference that goes alongside your testing workflow.

Get deep linking tips in your inbox

One email per week. No spam.

Ready to add deep linking to your app?

Set up Universal Links, App Links, deferred deep linking, and analytics in minutes. Free to start.