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.
Why Google changed how deep links work
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
Please enter a valid email address.
Thank you for Subscribing!
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 12 | What 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.
Step 2 - Create the assetlinks.json file
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
Step 3 - Deploy the assetlinks.json file to host(s)
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.
Step 4 - Verify that the assetlinks.json file is correct
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:
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:
- https://developer.android.com/about/versions/12/behavior-changes-all#web-intent-resolution
- https://developer.android.com/training/app-links
- https://handstandsam.com/2017/07/11/troubleshooting-auto-verification-of-seamless-android-app-deep-linking/
- https://zarah.dev/2017/01/20/testing-autoverify.html
- https://kenkyee.medium.com/full-verification-of-assetlinks-json-for-android-smartlock-app-browser-sharing-and-applinks-f66bd57207a4