Verify Android App Links

Android App Links are a special type of deep link that allow your website URLs to immediately open the corresponding content in your Android app (without requiring the user to select the app).

To add Android App Links to your app, define intent filters that open your app content using HTTP URLs (as described in Create Deep Links to App Content), and verify that you own both your app and the website URLs (as described in this guide). If the system successfully verifies that you own the URLs, the system automatically routes those URL intents to your app.

To verify ownership of both your app and your website, the following steps are required:

  • Request automatic app link verification in your manifest. This signals to the Android system that it should verify your app belongs to the URL domain used in your intent filters.
  • Declare the relationship between your website and your intent filters by hosting a Digital Asset Links JSON file at the following location:
    https://domain.name/.well-known/assetlinks.json
    .

You can find related information in the following resources:

  • Supporting URLs and App Indexing in Android Studio
  • Creating a Statement List
  • The difference between deep links and app links

    A deep link is an intent filter that allows users to directly enter a specific activity in your Android app. Clicking one of these links might open a disambiguation dialog, which allows the user to select one of multiple apps (including yours) that can hande the given URL. For example, figure 1 shows the disambiguation dialog after the user clicks a map link, asking whether to open the link in Maps or Chrome.

    Figure 1. The disambiguation dialog

    An Android App Link is a deep link based on your website URL that has been verified to belong to your website. So clicking one of these immediately opens your app if it's installed—the disambiguation dialog does not appear. Though the user may later change their preference for handling these links.

    The following table describes more specific differences.

    Deep linksApp links
    Intent URL scheme http, https, or a custom scheme Requires http or https
    Intent action Any action Requires android.intent.action.VIEW
    Intent category Any category Requires android.intent.category.BROWSABLE and android.intent.category.DEFAULT
    Link verification None Requires a Digital Asset Links file served on you website with HTTPS
    User experience May show a disambiguation dialog for the user to select which app to open the link No dialog; your app opens to handle your website links
    Compatibility All Android versions Android 6.0 and higher

    Request app links verification

    To enable link handling verification for your app, set android:autoVerify="true" in any one of the web URL intent filters in your app manifest that include the android.intent.action.VIEW intent action and android.intent.category.BROWSABLE intent category, as shown in the following manifest code snippet:

    <activity ...>
    
        <intent-filter android:autoVerify="true">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="http" android:host="www.example.com" />
            <data android:scheme="https" />
        </intent-filter>
    
    </activity>
    

    When android:autoVerify="true" is present on any one of your intent filters, installing your app on devices with Android 6.0 and higher causes the system to attempt to verify all hosts associated with the URLs in any of your app's intent filters. Verification involves the following:

    1. The system inspects all intent filters that include:
      • Action: android.intent.action.VIEW
      • Categories: android.intent.category.BROWSABLE and android.intent.category.DEFAULT
      • Data scheme: http or https
    2. For each unique host name found in the above intent filters, Android queries the corresponding websites for the Digital Asset Links file at https://hostname/.well-known/assetlinks.json.

    Only if the system finds a matching Digital Asset Links file for all hosts in the manifest does it then establish your app as the default handler for the specified URL patterns.

    Supporting app linking for multiple hosts

    The system must be able to verify every host specified in the app’s URL intent filters’ data elements against the Digital Asset Links files hosted on all the respective web domains. If any verification fails, the app is not verified to be a default handler for any of the URL patterns defined in the app's intent filters. The system then defaults to its standard behavior to resolve the intent, as described in Create Deep Links to App Content.

    For example, an app with the following intent filters would fail verification if an assetlinks.json file were not found at both https://www.example.com/.well-known/assetlinks.json and https://www.example.net/.well-known/assetlinks.json:

    <application>
    
      <activity android:name=”MainActivity”>
        <intent-filter android:autoVerify="true">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="http" android:host="www.example.com" />
          <data android:scheme="https" />
        </intent-filter>
      </activity>
      <activity android:name=”SecondActivity”>
        <intent-filter>
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="https" android:host="www.example.net" />
        </intent-filter>
      </activity>
    
    </application>
    

    Remember that all <data> elements in the same intent filter are merged together to account for all variations of their combined attributes. For example, the first intent filter above includes a <data> element that only declares the HTTPS scheme. But it is combined with the other <data> element so that the intent filter supports both http://www.example.com and https://www.example.com. As such, you must create separate intent filters when you want to define specific combinations of URI schemes and domains.

    Supporting app linking for multiple subdomains

    The Digital Asset Links protocol treats subdomains in your intent filters as unique, separate hosts. So if your intent filter lists multiple hosts with different subdomains, you must publish a valid assetlinks.json on each domain. For example, the following intent filter includes www.example.com and mobile.example.com as accepted intent URL hosts. So a valid assetlinks.json must be published at both https://www.example.com/.well-known/assetlinks.json and https://mobile.example.com/.well-known/assetlinks.json.

    <application>
      <activity android:name=”MainActivity”>
        <intent-filter android:autoVerify="true">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="https" android:host="www.example.com" />
          <data android:scheme="https" android:host="mobile.example.com" />
        </intent-filter>
      </activity>
    </application>
    

    Alternatively, if you declare your hostname with a wildcard (such as *.example.com), you must publish your assetlinks.json file at the root hostname (example.com). For example, an app with the following intent filter will pass verification for any sub-name of example.com (such as foo.example.com) as long as the assetlinks.json file is published at https://example.com/.well- known/assetlinks.json:

    <application>
      <activity android:name=”MainActivity”>
        <intent-filter android:autoVerify="true">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="https" android:host="*.example.com" />
        </intent-filter>
      </activity>
    </application>
    

    Declare website associations

    A Digital Asset Links JSON file must be published on your website to indicate the Android apps that are associated with the website and verify the app's URL intents. The JSON file uses the following fields to identify associated apps:

    • package_name: The application ID declared in the app's build.gradle file.
    • sha256_cert_fingerprints: The SHA256 fingerprints of your app’s signing certificate. You can use the following command to generate the fingerprint via the Java keytool:
      $ keytool -list -v -keystore my-release-key.keystore
      
      This field supports multiple fingerprints, which can be used to support different versions of your app, such as debug and production builds.

    The following example assetlinks.json file grants link-opening rights to a com.example Android app:

    [{
      "relation": ["delegate_permission/common.handle_all_urls"],
      "target": {
        "namespace": "android_app",
        "package_name": "com.example",
        "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"]
      }
    }]
    

    Associating a website with multiple apps

    A website can declare associations with multiple apps within the same assetlinks.json file. The following file listing shows an example of a statement file that declares association with two apps, separately, and resides at https://www.example.com/.well-known/assetlinks.json:

    [{
      "relation": ["delegate_permission/common.handle_all_urls"],
      "target": {
        "namespace": "android_app",
        "package_name": "com.example.puppies.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"]
      }
      },
      {
      "relation": ["delegate_permission/common.handle_all_urls"],
      "target": {
        "namespace": "android_app",
        "package_name": "com.example.monkeys.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"]
      }
    }]
    

    Different apps may handle links for different resources under the same web host. For example, app1 may declare an intent filter for https://example.com/articles, and app2 may declare an intent filter for https://example.com/videos.

    Note: Multiple apps associated with a domain may be signed with the same or different certificates.

    Associating multiple websites with a single app

    Multiple websites can declare associations with the same app in their respective assetlinks.json files. The following file listings show an example of how to declare the association of example.com and example.net with app1. The first listing shows the association of example.com with app1:

    https://www.example.com/.well-known/assetlinks.json

    [{
      "relation": ["delegate_permission/common.handle_all_urls"],
      "target": {
        "namespace": "android_app",
        "package_name": "com.mycompany.app1",
        "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 next listing shows the association of example.net with app1. Only the location where these files are hosted is different (.com and .net):

    https://www.example.net/.well-known/assetlinks.json

    [{
      "relation": ["delegate_permission/common.handle_all_urls"],
      "target": {
        "namespace": "android_app",
        "package_name": "com.mycompany.app1",
        "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"]
      }
    }]
    

    Publishing the JSON verification file

    You must publish your JSON verification file at the following location:

    https://domain.name/.well-known/assetlinks.json

    Be sure of the following:

    • The assetlinks.json file is served with content-type application/json.
    • The assetlinks.json file must be accessible over an HTTPS connection, regardless of whether your app's intent filters declare HTTPS as the data scheme.
    • The assetlinks.json file must be accessible without any redirects (no 301 or 302 redirects) and be accessible by bots (your robots.txt must allow crawling /.well-known/assetlinks.json).
    • If your app links support multiple host domains, then you must publish the assetlinks.json file on each domain. See Supporting app linking for multiple hosts.
    • Do not publish your app with dev/test URLs in the manifest file that may not be accessible to the public (such as any that are accessible only with a VPN). A work-around in such cases is to configure build variants to generate a different manifest file for dev builds.

    Test app links

    When implementing the app linking feature, you should test the linking functionality to make sure the system can associate your app with your websites, and handle URL requests, as you expect.

    To test an existing statement file, you can use the Statement List Generator and Tester tool.

    Confirm the list of hosts to verify

    When testing, you should confirm the list of associated hosts that the system should verify for your app. Make a list of all URLs whose corresponding intent filters include the following attributes and elements:

    • android:scheme attribute with a value of http or https
    • android:host attribute with a domain URL pattern
    • android.intent.action.VIEW category element
    • android.intent.category.BROWSABLE category element

    Use this list to check that a Digital Asset Links JSON file is provided on each named host and subdomain.

    Confirm the Digital Asset Links files

    For each website, use the Digital Asset Links API to confirm that the Digital Asset Links JSON file is properly hosted and defined:

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

    Testing a URL intent

    Once you have confirmed the list of websites to associate with your app, and you have confirmed that the hosted JSON file is valid, install the app on your device. Wait at least 20 seconds for the asynchronous verification process to complete. Use the following command to check whether the system verified your app and set the correct link handling policies:

    adb shell am start -a android.intent.action.VIEW \
        -c android.intent.category.BROWSABLE \
        -d "http://domain.name:optional_port"
    

    As part of your testing process, you can check the current system settings for link handling. Use the following command to get a listing of existing link-handling policies for all apps on your connected device:

    adb shell dumpsys package domain-preferred-apps
    

    Or the following does the same thing:

    adb shell dumpsys package d
    

    Note: Make sure you wait at least 20 seconds after installation of your app to allow for the system to complete the verification process.

    The command returns a listing of each user or profile defined on the device, preceded by a header in the following format:

    App linkages for user 0:
    

    Following this header, the output uses the following format to list the link-handling settings for that user:

    Package: com.android.vending
    Domains: play.google.com market.android.com
    Status: always : 200000002
    

    This listing indicates which apps are associated with which domains for that user:

    • Package - Identifies an app by its package name, as declared in its manifest.
    • Domains - Shows the full list of hosts whose web links this app handles, using blank spaces as delimiters.
    • Status - Shows the current link-handling setting for this app. An app that has passed verification, and whose manifest contains android:autoVerify="true", shows a status of always. The hexadecimal number after this status is related to the Android system's record of the user’s app linkage preferences. This value does not indicate whether verification succeeded.

    Note: If a user changes the app link settings for an app before verification is complete, you may see a false positive for a successful verification, even though verification has failed. This verification failure, however, does not matter if the user explicitly enabled the app to open supported links without asking. This is because user preferences take precedence over programmatic verification (or lack of it). As a result, the link goes directly to your app, without showing a dialog, just as if verification had succeeded.

    Test example

    For app link verification to succeed, the system must be able to verify your app with all of the websites that you specify in your app’s intent filters, and that meet the criteria for app links. The following example shows a manifest configuration with several app links defined:

    <application>
    
        <activity android:name=”MainActivity”>
            <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="https" android:host="www.example.com" />
                <data android:scheme="https" android:host="mobile.example.com" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="https" android:host="www.example2.com" />
            </intent-filter>
        </activity>
    
        <activity android:name=”SecondActivity”>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="https" android:host="account.example.com" />
            </intent-filter>
        </activity>
    
          <activity android:name=”ThirdActivity”>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="https" android:host="map.example.com" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="market" android:host="example.com" />
            </intent-filter>
          </activity>
    
    </application>
    

    The list of hosts that the platform would attempt to verify from the above manifest is:

    www.example.com
    mobile.example.com
    www.example2.com
    account.example.com
    

    The list of hosts that the platform would not attempt to verify from the above manifest is:

    map.example.com (it does not have android.intent.category.BROWSABLE)
    market://example.com (it does not have either an “http” or “https” scheme)
    

    To learn more about statement lists, see Creating a Statement List.