Skip to content

Blog


Your Deep Links Might Be Broken: Web Intents and Android 12

January 25, 2022

|
Michael Yotive

Michael Yotive

Part of being a modern-day Android developer is keeping up with Google’s latest update that can have outsized effects and consequences on application’s performance, especially when new changes affect how older app versions function. 

Generally, Google releases two types of changes that impact developers: API changes and behavior changes. 

Fortunately, API changes tend to be obvious. They are primarily coding changes that trigger compilation errors or lint warnings. 

Behavior changes, on the other hand, are not so obvious, particularly when Google changes or adds constraints around an established feature, such as a background execution. If developers don’t pay attention when a new version of Android is released, the number of bug reports or QA tickets stating that a feature is no longer working as intended can skyrocket. 

Fortunately, Google provides a list of all the behavior changes in a given release

In this post, we take a look at changes that started in Android 12 that alter how generic web intents resolve. Because many applications use outside web links that direct users into the application – a process called deep linking – there are key fixes required in light of the latest Android updates.  We’ll go over why the change was made to web intents and what developers need to do to accommodate it. While the coding changes are small, there are background processes taking place that must be kept in mind to achieve optimal outcomes. 

Deep links help us provide a seamless experience between the DoorDash website and the mobile application. Assuming the application is installed, when a user taps a link from an email client or search engine results, we want the Android device to route the request directly to the DoorDash application instead of the web experience.

As a practical example, let’s say a user taps on https://www.doordash.com/store/fusian-columbus-76690/ from a google search result. Android dispatches a “generic web intent,” that routes the user directly to the store within the DoorDash application. 

However, according to the new android version:

Starting in Android 12 (API level 31), a generic web intent resolves to an activity in your app only if your app is approved for the specific domain contained in that web intent. If your app isn't approved for the domain, the web intent resolves to the user's default browser app instead.

Why did Google make this change to web intents?

In Android 6, Google gave us the ability to create Android App Links, allowing developers to designate their apps as the default handler for a given link, such as an HTTP link. If your application is the default handler, then Android will open your application immediately. Otherwise, a different handler or application will be used. 

Now, starting in Android 12, Google requires you to use Android App Links for HTTP and HTTPS links. Not utilizing Android App Links will result in links always being shown in a web browser and not your application.

We will explain step by step how generic web intents work and how to use Android App Links to resolve this issue.

Stay Informed with Weekly Updates

Subscribe to our Engineering blog to get regular updates on all the coolest projects our team is working on

Understanding generic web intents

In Android, there are two types of intents: explicit and implicit. Explicit intents happen when you know specifically the Android component you want to launch. DoorDash uses these frequently to start activities within our applications.

Implicit intents, on the other hand, are when the desired component is not known or when Android is asked to find an application to handle a request. 
The following is an example of asking Android to seek an application to handle an intent:

val webpage: Uri = Uri.parse("https://www.doordash.com/store/fusian-columbus-76690/")
val intent = Intent(Intent.ACTION_VIEW, webpage)
startActivity(intent)

Android’s activity manager uses this intent to scan through activities to try to find one that can handle Intent.ACTION_VIEW and data of a particular URL, which, in this case, is a DoorDash URL. Most of the time, this intent will resolve to a web browser to view the web page. Ultimately, this is close to the intent triggered when a user clicks on a link within a browser or email client.

 Resolving generic web intents

To instruct Android to use a local activity for this intent, insert an intent filter inside the android manifest as shown here:

<activity
    android:name="com.doordash.DeepLinkActivity"
    android:theme="@style/Theme.Consumer.DoorDash">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />

            <category android:name="android.intent.category.BROWSABLE" />
            <category android:name="android.intent.category.DEFAULT" />

            <data
                android:host="www.doordash.com"
                android:pathPrefix="/store"
                android:scheme="https" />

        </intent-filter>
</activity>

This way, Android can see and attempt to use the DeepLinkActivity because it has an intent filter with the action of Intent.ACTION_VIEW and a data field matching the requested URL. This can be useful to establish a connection between an HTTP link and the application. 

Until the advent of Android 12, we used this technique at DoorDash to navigate the user directly into a specific store, the user’s cart, various account features, and many other features as shown here:

What happens on Android 12What should happen

Because of the behavior change in Android 12, we now are required to do some additional work to accomplish deep linking. 

Following the deep linking path

Our solution begins with creating a digital link between the application and the domain for the Android system to verify. Below are the step by step instructions. 

Step 1 - Update the Android Manifest

First we must update all intent filters that can respond to an HTTP link with the android:autoVerify=”true attribute:

<activity
    android:name="com.doordash.DeepLinkActivity"
    android:theme="@style/Theme.Consumer.DoorDash">
        <intent-filter android:autoVerify="true" tools:targetApi="m">
...
        </intent-filter>
</activity>

Optionally add tools:targetApi=”m” to appease the Lint warning

According to Google, the autoVerify attribute “allows your app to designate itself as the default handler of a given type of link. So when the user clicks on an Android App Link, your app opens immediately if it's installed — the disambiguation dialog doesn't appear.”

In practice, this means the Android system will securely verify ownership of the link you are trying to process. The system uses a digital asset links file to accomplish this. Asset links are a protocol that securely captures statements made by digital assets such as websites or mobile apps and provides information about the statements’ relationship with other digital assets. Deploying this protocol is straightforward; simply generate the digital asset link file described in the next step.

There are three options for creating the digital asset link file.

Option 1 - Create the assetlinks.json file manually

  • Create an empty file named assetlinks.json
  • If working with a single application, place the following information inside this file:
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "<Your App’s package name>",
    "sha256_cert_fingerprints":
    ["<Your App’s SHA256 finger print>"]
  }
}]

If there are multiple applications addressed on your domain, use this instead:

 [{
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.dd.doordash",
      "sha256_cert_fingerprints": ["93:6F:83:B9:14:21:6D:8A:87:A7:97:EF:FB:5C:A9:D4:50:0B:D2:78:D8:92:07:9F:DB:0D:5D:05:FE:F2:10:B5"]
    }
  },
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.trycaviar.customer",
      "sha256_cert_fingerprints": ["2A:59:23:CF:17:46:ED:DC:12:31:3A:99:6A:A3:8D:11:A7:56:7B:08:7E:74:A6:F0:B3:A5:60:81:63:FA:7B:D0"]
    }
  }
]

To find the application’s package, look for the application ID declared in the app's build.gradle file or if the apk is available, use the aapt command: aapt dump badging [path-to-apk].

To find the SHA256 fingerprint, use Java’s keytool command. If the keystore is available, use the following: keytool -list -v -keystore [my-release-key.keystore]. Or if working with the .apk file, use this: keytool -printcert -jarfile [path-to-apk]

Option 2 - Use Android Studio’s App Link Assistant

Android Studio provides a tool for generating the assetlinks.json file for one application, automatically finding the app’s package name and SHA256 fingerprint. If there are multiple applications, the generated file must be updated manually with the other applications.

Option 3 - Use Google’s statement list generator tool

Similar to Android Studio’s tool, Google’s statement list generator uses the package name and SHA 256 to generate the assetlinks.json file automatically. The details are available in this link: https://developers.google.com/digital-asset-links/tools/generator

After generating the digital asset links file, deploy it to the host (or hosts if handling multiple domains) under a .well_known directory such as https://www.doordash.com/.well-known/assetlinks.json

Google provides some useful information to watch out for when hosting this file:

  • The assetlinks.json file must be accessible without any redirects (no 301 or 302 redirects) and must be accessible by bots (your robots.txt must allow crawling /.well-known/assetlinks.json).
  • The assetlinks.json file must be served with content-type application/json.
  • The assetlinks.json file must be accessible over an HTTPS connection, regardless of whether the app's intent filters declare HTTPS as the data scheme.

Now that we’ve generated the assetlinks.json file, how do we confirm that the file is correct? Luckily, Google provides a Digital Asset Links API to verify the accuracy of the assetlinks.json file: 

https://digitalassetlinks.googleapis.com/v1/statements:list? source.web.site=<https://domain.name:optional_port>&relation=delegate_permission/common.handle_all_urls

As a practical example, here is DoorDash’s link:

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

If all goes well, the API will respond with the following:

Step 5 - Verify your changes on Android

Congratulations! The Android Manifest is updated and the app is associated with the assetlinks.json file required by Google. After redeploying the app, test the deep link process using the command line:

adb shell am start -a android.intent.action.VIEW \
    -c android.intent.category.BROWSABLE \
    -d "https://www.doordash.com/store/fusian-columbus-76690/"

This ADB command launches an implicit intent with the action VIEW, category BROWSABLE, and data containing a DoorDash store, all of which is similar to the implicit intent executed when clicking on a link.

If there are still glitches at this point try manually invoking the domain verification on the device or emulator. Google lists the steps here as well as the possible return values.

Conclusion

To summarize, here are the key takeaways from this post: 

  • Utilize android:autoVerify=”true” in the Android Manifest within intent filters that respond to intents with http links.
  • Generate an assetlinks.json digital asset file to create a link between the host and the application. 
  • Work with your infrastructure team to deploy the assetlinks.json file to the host(s) or hosts (if you are handling multiple domains).

Google’s documentation can be perilous for inexperienced Android developers, particularly when behavior changes are involved. We hope this guide will help you steer clear of any issues around deeplinking. If you are interested in directly impacting the end user’s experience check out our open roles on our career page 

References:

About the Author

Related Jobs

Location
San Francisco, CA; Sunnyvale, CA; Seattle, WA
Department
Engineering
Location
Sunnyvale, CA; San francisco, CA
Department
Engineering
Location
Sunnyvale, CA; San francisco, CA
Department
Engineering
Location
Sunnyvale, CA; San francisco, CA
Department
Engineering
Job ID: 2915998
Location
Sao Paulo, Brazil
Department
Engineering