Introduction

What do you do when your Android App Links don’t work the way you want them to? There are many tools available from Google to help you, such as the Play Console Deep Links page which gives you a single place to view and resolve many common issues with App Links. After being notified of an issue in the Play Console, you can follow these steps to debug your Android App Links. This blog post will walk you through debugging deep links in an application using adb, inspect your Digital Asset Link files using curl, and demonstrate how to handle user experience on the device.

Let’s fire up the terminal. We’re running adb commands!

Symptom 1: The Links Never Work

If users are clicking on links and they aren’t opening in your application, then there might be a problem with your intent-filters. You can test your intent-filters using the command <a class="au mc" href="https://developer.android.com/training/app-links/deep-linking#testing-filters" target="_blank" rel="noopener ugc nofollow">adb shell am start</a> with a VIEW action, a deep linked URL, and the package id. This checks that the application can handle a deep link which is explicitly sent to it.

This command should open the application if its intent-filters are configured correctly. However, if you see an error like the one below, you should inspect the AndroidManifest.xml file and double check its configuration.

Error: Activity not started, unable to resolve Intent { act=android.intent.action.VIEW dat=https://droidfood.example.com/... flg=0x10000000 pkg=com.example.droidfood }

In an Android app’s AndroidManifest.xml file, Android App Links are declared as intent-filter elements. When they are correctly configured, the Android app will be the default handler for links that match the intent-filter parameters.

Correctly configuring these parameters involves:

  • Setting the <a class="au mc" href="https://developer.android.com/reference/android/R.styleable#AndroidManifestIntentFilter_autoVerify" target="_blank" rel="noopener ugc nofollow">autoVerify</a> attribute of the intent-filter element to true
  • Setting an action element with the name attribute to VIEW
  • Setting a category element with the name attribute to BROWSABLE
  • Setting a second category element with the name attribute to DEFAULT
  • Including at least one data element with a scheme attribute of “HTTP” or “HTTPS”
  • Including a data element with a “host” attribute set to the domain where you host your deep links
  • Having no data element with a custom scheme

For example, the following is a correctly-configured intent-filter to handle deep links that match https://droidfood.example.com:

Once the application’s intent-filters are correctly configured, rerun the adb command and ensure that the application opens correctly.

Once it does, try the command again without the explicit package name ‘com.example.droidfood’. This tests that Android can open the application with the link correctly.

If the application opens, then you’ve fixed the issue! If, like in the image below, the system web browser opens the link you have more debugging to do.

Symptom 2: Links Open in the Browser (But Work With ADB)

The system web browser has handled the link instead. The next two commands, introduced in Android 12, should help diagnose what happened:.

  • adb shell pm verify-app-links -- re-verify forces the Android operating system to verify the site association between the application and the web host. This is an asynchronous command and may take a few minutes for its work to be completed.
  • adb shell pm get-app-links lists Android App Links declared in the manifest and their verification status.

In Android, HTTP links must be verified for apps to be the default handler for a deep link. Both of these commands take the package name of the application as a parameter. Running these commands for the app gives us this result:

The links are Android App Links; however, they are not “verified” Android App Links and the application won’t be the default handler for links for the droidfood.example.com host. The Android developer documentation provides a full list of the verification results and how to interpret them.

Android uses the Digital Asset Links API to establish trust between a web domain and the application. In practice, this means that each host in an app’s set of intent-filter elements in AndroidManifest.xml serves an assetlinks.json file in the /.well-known/ path.

The assetlinks.json file establishes trust by providing the following information:

  • The package name of the Android application
  • An array of signing key certificate fingerprints that identify the application
  • The application signing key certificate fingerprint must match one in this array

The curl command can easily fetch files from a host and can be used to check the content of the file.

Additionally, the hosting for the file must meet several criteria:

  • assetlinks.json must be served publicly and cannot be behind authentication or a VPN
  • The host must serve the file using a secure HTTPS connection
  • Redirects may not be used to access assetlinks.json
  • The Content-Type HTTP header of the file must be application/json

curl can also be used to check that a file is correctly hosted. The -i option should be used to print the HTTP headers which can be checked for correctness

The assetlinks.json file seems to be correctly hosted, and the content looks correct at a glance. There are several checks that can narrow down what problems might be causing links to fail. If the file isn’t correctly hosted, check “Section 3: Diagnosing Other Problems” for more information.

Confirming Our Signing Keys

The assetlinks.json file for https://droidfood.example.com references the certificate fingerprint “4C:D7:E4:04:B8:54:04:A0:5F:50:35:38:CF:B6:89:3D:35:CE:54:C7:01:8E:ED:78:7A:50:9B:4A:2E:A8:BE:B4”.

In the development lifecycle of an Android app, many keys are used to sign the application. One common source of errors is that the key used when signing an APK in the Play Store doesn’t match the key that’s referenced in the assetlinks.json file.

“If you’re using Play App Signing for your app, then the certificate fingerprint produced by running keytool locally will usually not match the one on users’ devices. You can verify whether you’re using Play App Signing for your app in your Play Console developer account under Release > Setup > App Integrity; if you do, then you’ll also find the correct Digital Asset Links JSON snippet for your app on the same page.”

The command adb shell pm get-app-links displays the certificate fingerprint used by Android to verify links, and this can be compared to the certificate used in assetlinks.json:

In this example, the fingerprints do not match. To fix the error, apply the correct fingerprint needs to the assetlinks.json file. To test the fix, use adb commands to reverify the links:

Updating the certificate fingerprint in the assetlinks.json file has fixed the issue! Now the application should open instead of the browser when your app receives a view intent. This can be tested with adb using the same command we used in Section 1:

For more information on key generation please see “Sign your app” on developer.android.com.

Symptom 3: Your Links Used to Work But Don’t Now

There are many different providers, services, and server applications that may be hosting assetlinks.json. Additionally there may be content delivery networks or other similar services that may be interfering with link verification. The next few sections show how to detect these failures, but don’t investigate how to solve them. That task is better suited for a host’s operations team than an Android app developer.

Each example uses the curl tool, and highlights what output to be aware of in the case of hosting errors.

Incorrectly Hosted File (Using a Redirect)

A 301 redirect like the one seen above will cause verification to fail. This is often the case when an assetlinks.json file is hosted on a server with one domain, but the app’s intent-filter handles a different domain. For example, the Android App Links may handle “example.com” but the content redirects to “droidfood.example.com”.

There are several other common redirect headers which will also block verification. The IETF HTTP RFC specifies other 3xx redirects that may be encountered. Remember, the assetlinks.json file must return a HTTP Status of 200 to be successfully validated.

Missing HTTPS

Port 443 is the standard port for encrypted HTTPS communication. The above output demonstrates a failure to load the assetlinks.json file because the server does not use HTTPS. Android will not verify links if the unencrypted HTTP protocol is used.

Incorrect Content-Type Header

In the above example, the file sets the content-type header to text/plain (plaintext) and not JSON. Android App Links require the assetlinks.json file to be served with a HTTP Content-Type header value of application/json.

Private assetlinks.json File

The assetlinks.json file must be public. Development, testing, and debugging often happens while connected to an office VPN. This can create a situation where files are accessible during testing and debugging to app maintainers, but are not available to the public. To verify that your app can load the assetlinks.json file, use a public network without a VPN connection. Examples of these networks include: a phone’s cellular network, a home connection, and the Wi-Fi network at a local coffee shop.

Missing or Misplaced assetlinks.json File

Every host referenced by an intent-filter with the autoVerify attribute set to true in an AndroidManifest.xml file must also have an assetlinks.json file that establishes an association between that domain and the application. Additionally, if wildcards are used in the intent filter element, then the assetlinks.json file must be available in the host’s top-level /.well-known/ directory. For example, *.example.com will fail validation if example.com does not host the assetlinks.json file.

Other Server Errors

We’ve presented common problems that developers have reported., Check your server for other types of unexpected behavior that could impact verification of assetlinks.json

Symptom 4: The Disambiguation Dialog Appears

The disambiguation dialog appears when the Android system cannot determine which app Activity should handle an Intent. Even after confirming that all links have correct intent filters, all hosts have assetlinks.json files, and the files are correctly hosted, the disambiguation dialog might appear. This section describes common scenarios where the Android system cannot determine the correct Activity. You can use this information to reduce the number of times that users see the disambiguation dialog.

Intent Collisions

The following example defines two activities: DessertActivity and FoodActivity. Both handle the deep link “https://droidfood.example.com/food/dessert” because this link matches both of their pathPrefix attributes.

When multiple activities in an app define intent-filters for the same link, the disambiguation dialog can appear. Intent-filters should not overlap among different activities, and if this is an intentional feature of the application, the “label” attribute on the “activity” element should be set to differentiate which activity will handle the link intent.

Android 12 Changes

Before Android 12, if any links in your app failed verification, or all intent-filters lacked “autoVerify=true” then those Android App Links would show the disambiguation dialog. In Android 12, however, these links will instead be handled by the default web browser (see Android 12 Web intent resolution behavior changes). To avoid the disambiguation dialog on earlier versions of Android, make sure all of your Android App Links can be verified, and remove test intent-filters from your AndroidManifest.xml.

Other Apps Handle the Same Links

Sometimes a user will install a third party application that handles the same links as another application. If both applications are using matching custom schemes, the disambiguation dialog appears. To avoid this, use web links with autoVerify=”true” set and add the assetlinks.json file to each domain that hosts your Android App Links.

The User Has Chosen a Different Default App

The user can choose to have a link always opened by a specific app. Your app can detect this and show an educational UI that advises users to associate itself with these deep links.

The following example shows how to detect whether the application with a package id “com.example.droidfood” is the default handler for the link “https://droidfood.example.com/about”.

This code uses the PackageManager to find the default handler for a link you expect to handle. The code then compares this package name to the package name of our application. If names match, then the user has chosen our app to handle the Android App Links for the “https://droidfood.example.com/about” domain. Otherwise, the user changed their default handler.

Continue Learning

This post shows how to detect and handle common errors with deep links. In the future we will also explore the new Deep Links Play Console which gives developers live feedback on their deep link performance and common errors like the ones in this post. There are many UX design patterns and best practicadb shell pm verify-app-links — re-verifyes that we’ve documented in DAC to help you make your app the best it can be!

Happy Linking!

By Summers Pittman
Source Medium

Previous Managing Kyverno Policies As OCI Artifacts With OCIRepository Sources
Next Intel NUC 12 Enthusiast Delivers Powerful Mini PC