{"id":547,"date":"2026-03-19T17:00:00","date_gmt":"2026-03-19T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=547"},"modified":"2026-03-07T03:32:43","modified_gmt":"2026-03-07T08:32:43","slug":"android-12-app-links-changes","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/android-12-app-links-changes\/","title":{"rendered":"Android 12+ App Links Changes You Need to Know"},"content":{"rendered":"\n<p>Android 12 (API level 31) introduced several changes to how App Links verification works and how users can control which app handles a given URL. Some of these changes are improvements that make verification more reliable. Others introduced new requirements that broke existing implementations if developers were not paying attention.<\/p>\n\n\n\n<p>If you are targeting API 31 or higher, or if you noticed deep link issues starting with Android 12 devices, this guide explains each change in detail and tells you what you need to update.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1080\" height=\"720\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/android-12-app-links-changes-inline-0.jpg\" alt=\"a cell phone sitting on top of a table\" class=\"wp-image-506\" srcset=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/android-12-app-links-changes-inline-0.jpg 1080w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/android-12-app-links-changes-inline-0-300x200.jpg 300w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/android-12-app-links-changes-inline-0-1024x683.jpg 1024w, https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/android-12-app-links-changes-inline-0-768x512.jpg 768w\" sizes=\"auto, (max-width: 1080px) 100vw, 1080px\" \/><figcaption class=\"wp-element-caption\">Photo by <a href=\"https:\/\/unsplash.com\/@pqr_uniqr?utm_source=tolinku&#038;utm_medium=referral\" rel=\"nofollow noopener\" target=\"_blank\">Pete Kim<\/a> on <a href=\"https:\/\/unsplash.com\/?utm_source=tolinku&#038;utm_medium=referral\" rel=\"nofollow noopener\" target=\"_blank\">Unsplash<\/a><\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">The Biggest Change: Per-Domain Verification<\/h2>\n\n\n\n<p>On Android 11 and earlier, App Links verification was all-or-nothing within an intent filter. If your app declared three domains in a single intent filter and verification failed for one of them (due to a misconfigured <code>assetlinks.json<\/code> file or a server error), Android would deny verified status for all three domains in that filter. One broken domain could take down App Links for your entire app.<\/p>\n\n\n\n<p>Android 12 changed this to per-domain verification. Each domain is now verified independently, and a failure for one domain does not affect the others. Your app receives verified App Link status for every domain that passes, even if some domains fail.<\/p>\n\n\n\n<p>This is a meaningful improvement for apps that handle multiple domains. It also means that fixing a broken <code>assetlinks.json<\/code> for one domain will restore verified status for that domain without requiring a full reinstall on Android 12 devices.<\/p>\n\n\n\n<p>The <a href=\"https:\/\/developer.android.com\/training\/app-links\/verify-android-applinks\" rel=\"nofollow noopener\" target=\"_blank\">Android developer documentation on App Links verification<\/a> covers the updated verification flow in detail.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Server-Side Pre-Verification<\/h2>\n\n\n\n<p>On Android 11 and earlier, each device fetched the <code>assetlinks.json<\/code> file independently at install time. On Android 12 and later, Google&#39;s servers fetch and cache the <code>assetlinks.json<\/code> files, and devices receive pre-verified results. This means:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Verification can succeed even on devices without internet access at install time, because the result comes from Google&#39;s cache.<\/li>\n<li>Changes to your <code>assetlinks.json<\/code> file propagate faster than before, since Google&#39;s servers re-fetch periodically rather than waiting for user reinstalls.<\/li>\n<li>Your <code>assetlinks.json<\/code> must be publicly accessible to Google&#39;s infrastructure. If the file is behind a firewall, a VPN, or any kind of IP-based access control, verification will fail silently on Android 12 devices even though on-device fetching might have worked before.<\/li>\n<\/ul>\n\n\n\n<p>Check that <code>https:\/\/yourdomain.com\/.well-known\/assetlinks.json<\/code> is reachable from outside your network. The <a href=\"https:\/\/developers.google.com\/digital-asset-links\/v1\/getting-started\" rel=\"nofollow noopener\" target=\"_blank\">Google Digital Asset Links API<\/a> provides a verification endpoint you can use to test this:<\/p>\n\n\n\n<pre><code>https:\/\/digitalassetlinks.googleapis.com\/v1\/statements:list?source.web.site=https:\/\/yourdomain.com&amp;relation=delegate_permission\/common.handle_all_urls\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Link Handling Preferences: The New User Control<\/h2>\n\n\n\n<p>Android 12 gave users more direct control over which app handles specific domains. In the system settings for any app, users can now see the domains that app is verified to handle and can disable that handling on a per-domain basis.<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/doc-android-open-by-default-settings.svg\" alt=\"Android open by default settings screen for app link domain verification\"><\/p>\n\n\n\n<p><em>Source: <a href=\"https:\/\/developer.android.com\/training\/app-links\/verify-android-applinks\" rel=\"nofollow noopener\" target=\"_blank\">Android Developer Documentation<\/a><\/em><\/p>\n\n\n\n<p>This is accessible at: <strong>Settings &gt; Apps &gt; [Your App] &gt; Open by default<\/strong>.<\/p>\n\n\n\n<p>The page shows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Whether the app is set as the default for any links<\/li>\n<li>Which domains the app has verified status for<\/li>\n<li>An &quot;Add link&quot; option to manually associate additional domains<\/li>\n<li>An option to remove domains from the app&#39;s default handling<\/li>\n<\/ul>\n\n\n\n<p>For users who prefer to have links open in a browser rather than in an app, this control is welcome. For developers, it means that even a perfectly verified App Link can be overridden by the user. Your app cannot force itself to be the default handler.<\/p>\n\n\n\n<p>The practical implication: if a user taps one of your App Links and explicitly chooses to open it in a browser from the disambiguation dialog, Android 12 remembers that preference for that domain. Future taps will go to the browser without asking again. The user would have to manually restore your app as the default in Settings.<\/p>\n\n\n\n<p>There is no API for querying whether a user has overridden your app&#39;s default status for a domain. You can check whether your app is currently verified, but not whether the user has chosen to override that verification.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The android:exported Requirement<\/h2>\n\n\n\n<p>This is the change that broke the most existing apps immediately on Android 12: any <code>&lt;activity&gt;<\/code>, <code>&lt;service&gt;<\/code>, or <code>&lt;broadcast-receiver&gt;<\/code> that declares an <code>&lt;intent-filter&gt;<\/code> now requires an explicit <code>android:exported<\/code> attribute.<\/p>\n\n\n\n<p>Before Android 12, if you omitted <code>android:exported<\/code>, Android would infer it as <code>true<\/code> if the component had intent filters. Starting with API 31, omitting the attribute causes an install failure:<\/p>\n\n\n\n<pre><code>INSTALL_FAILED_MISSING_EXPORTED_FLAG\n<\/code><\/pre>\n\n\n\n<p>For deep link activities, you almost always want <code>android:exported=&quot;true&quot;<\/code>, since the whole point is for external intents (from browsers, other apps) to be able to launch your activity:<\/p>\n\n\n\n<pre><code class=\"language-xml\">&lt;activity\n    android:name=&quot;.MainActivity&quot;\n    android:exported=&quot;true&quot;&gt;\n    &lt;intent-filter android:autoVerify=&quot;true&quot;&gt;\n        ...\n    &lt;\/intent-filter&gt;\n&lt;\/activity&gt;\n<\/code><\/pre>\n\n\n\n<p>If you have internal activities with intent filters that should not be reachable from outside your app, set <code>android:exported=&quot;false&quot;<\/code>. But for any activity handling deep links or App Links, use <code>true<\/code>.<\/p>\n\n\n\n<p>This requirement applies when your <code>targetSdkVersion<\/code> is 31 or higher. If you are still targeting 30, you are not affected yet, but you will need to address this before raising your target SDK.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Web Intent Resolution Changes<\/h2>\n\n\n\n<p>Android 12 also changed how web intents are resolved when an app is not installed or not verified. Prior to Android 12, a URL that matched an intent filter (even without verification) would sometimes still preferentially open in an app over a browser if the user had previously chosen that app. The new behavior is stricter: unverified intents that match HTTPS URLs default to the browser unless the user has explicitly set the app as the default handler.<\/p>\n\n\n\n<p>This change reinforces the importance of proper App Links verification. Links that worked &quot;by habit&quot; on older Android versions (because the user had previously selected the app from the disambiguation dialog) may now default to the browser on Android 12 devices, because the implicit preference is no longer carried forward in the same way.<\/p>\n\n\n\n<p>See <a href=\"https:\/\/tolinku.com\/blog\/verified-vs-unverified-app-links\/\">Verified vs Unverified App Links on Android<\/a> for a detailed comparison of what the user experiences in each case.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Package Visibility Restrictions<\/h2>\n\n\n\n<p>Android 11 introduced package visibility restrictions (<code>&lt;queries&gt;<\/code> in the manifest), and Android 12 refined these further. While this does not directly affect App Links verification, it matters if your app opens URLs programmatically and needs to determine which app will handle a given intent.<\/p>\n\n\n\n<p>If you call <code>PackageManager.queryIntentActivities()<\/code> or similar methods to check which apps can handle a URL, you need to declare the relevant package names or intent queries in your manifest:<\/p>\n\n\n\n<pre><code class=\"language-xml\">&lt;queries&gt;\n    &lt;intent&gt;\n        &lt;action android:name=&quot;android.intent.action.VIEW&quot; \/&gt;\n        &lt;data android:scheme=&quot;https&quot; \/&gt;\n    &lt;\/intent&gt;\n&lt;\/queries&gt;\n<\/code><\/pre>\n\n\n\n<p>Without the appropriate <code>&lt;queries&gt;<\/code> declaration, your app may not be able to see other installed apps that could handle a URL, even if they are installed. The system will still route the URL correctly when you actually fire the intent; this restriction only affects pre-launch queries about what can handle a given intent.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Re-verification Without Reinstall<\/h2>\n\n\n\n<p>On Android 12 and later, developers and testers can manually trigger App Links verification using <code>adb<\/code> without reinstalling the app. This is useful when debugging or after updating the <code>assetlinks.json<\/code> file:<\/p>\n\n\n\n<pre><code class=\"language-bash\">adb shell pm verify-app-links --re-verify com.example.myapp\n<\/code><\/pre>\n\n\n\n<p>After running this command, check the verification result:<\/p>\n\n\n\n<pre><code class=\"language-bash\">adb shell pm get-app-links --package com.example.myapp\n<\/code><\/pre>\n\n\n\n<p>The output shows each domain and its status: <code>verified<\/code>, <code>approved<\/code>, <code>rejected<\/code>, or <code>none<\/code>. On Android 11 and earlier, the only reliable way to re-trigger verification was to uninstall and reinstall the app. The <code>adb<\/code> command makes the iteration cycle much faster.<\/p>\n\n\n\n<p>For a full breakdown of testing commands and debugging workflows, see the <a href=\"https:\/\/tolinku.com\/blog\/testing-android-app-links\/\">Tolinku Android App Links testing guide<\/a> and the <a href=\"https:\/\/tolinku.com\/docs\/troubleshooting\/android\/\">Tolinku troubleshooting documentation<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary of Required Actions for Android 12+<\/h2>\n\n\n\n<p>If you are updating an existing App Links implementation or building for Android 12 and later, here is a checklist:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Add <code>android:exported=&quot;true&quot;<\/code> to every activity that declares an intent filter. Your build will fail on API 31 targets if you miss this.<\/li>\n<li>Confirm your <code>assetlinks.json<\/code> is publicly reachable from Google&#39;s servers, not just from your local network or corporate VPN.<\/li>\n<li>Use <code>adb shell pm verify-app-links --re-verify<\/code> during development instead of reinstalling the app each time you update the <code>assetlinks.json<\/code> file.<\/li>\n<li>Accept that users now have explicit per-domain control over link handling. Design your app&#39;s web fallback experience accordingly, since some users may prefer the browser.<\/li>\n<li>If you are programmatically querying for intent handlers, add the appropriate <code>&lt;queries&gt;<\/code> elements to your manifest.<\/li>\n<\/ol>\n\n\n\n<p>The per-domain verification improvement is the most significant positive change. If you had a multi-domain setup that was partially broken before, Android 12 means fixing one domain no longer requires fixing all of them first.<\/p>\n\n\n\n<p>For full implementation guidance, see the <a href=\"https:\/\/tolinku.com\/docs\/developer\/app-links\/\">Tolinku App Links developer documentation<\/a> and the <a href=\"https:\/\/tolinku.com\/blog\/android-app-links-complete-guide\/\">Android App Links complete guide<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Android 12 changed how App Links verification works, how users can override link handling preferences, and how apps declare exported components. If your deep links were working before Android 12 and are now broken, this guide explains what changed and what to fix.<\/p>\n","protected":false},"author":2,"featured_media":546,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Android 12+ App Links Changes You Need to Know","rank_math_description":"Android 12 changed App Links domain verification, link handling preferences, and exported attributes. Learn what changed and how to update your implementation.","rank_math_focus_keyword":"android 12 app links","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-android-12-app-links-changes.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-android-12-app-links-changes.png","footnotes":""},"categories":[10],"tags":[25,23,20,34,33],"class_list":["post-547","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","tag-android","tag-app-links","tag-deep-linking","tag-kotlin","tag-user-experience"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/547","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=547"}],"version-history":[{"count":2,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/547\/revisions"}],"predecessor-version":[{"id":2103,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/547\/revisions\/2103"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/546"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=547"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=547"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=547"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}