Reputation: 1224
My app defines the intent filters to handle URL's from my site defined by
<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:host="www.host.com" android:scheme="http"/>
</intent-filter>
<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:host="www.host.com" android:scheme="https"/>
</intent-filter>
The app correctly detects URL's of the correct host but queries the user whether to open them in the app or browser. I tried using the App links verification as specified here: https://developer.android.com/training/app-links/index.html
As seen in my server logs, when installing the app, the device queries /well-known/assetlinks.json and it responds with a 200 status. Testing the digital assets file using the
https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://<domain1>:<port>&relation=delegate_permission/common.handle_all_urls
API and it found no errors.
The SHA256 in the assetlinks.json file was obtained using
keytool -list -v -keystore my-release-key.keystore
the same .keystore of which the app was signed.
Running adb shell dumpsys package d
returns that the link verification status is "ask" meaning that verification failed. Why might verification be failing?
Upvotes: 50
Views: 47900
Reputation: 28845
Looking at j__m comment, I found this.
In AndroidManifest
write so:
<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" />
<!-- Write <data> tags with one attribute, if you use several domains. -->
<data android:scheme="https" />
<data android:host="example.com" />
</intent-filter>
<!-- Other domains-->
<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" />
<data android:host="server.com" />
</intent-filter>
android:autoVerify="true"
is needed for App Linking.
Create assetlinks.json
using Tools > App Links Assistant
. Then press Open Digital Asset Links File Generator
, enter domain, application id, select release
signing config and press Generate Digital Asset Links File
. Then you can save file or copy to clipboard.
You can create several assetlinks.json
files (for several applications) and join them into one JSON. To my mind it doesn't depend on Windows line endings (I used Notepad to concatenate JSONs). First time I auto-formatted it with Ctrl + Alt + L, and after uploading to domains App Link didn't work (probably because of later errors in AndroidManifest
), so on the 2nd try I didn't format JSON. I created assetlinks.json
for release
and debug
builds of the application.
Upload assetlinks.json
to https://example.com/.well-known/assetlinks.json
(in this answer I write: example.com
and mean your domain like company.name
). Check it with https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls
. The file and the domain have some restrictions. In my case everything was simple, we didn't change settings.
In your DeepLinkActivity
you can parse URLs with regular expressions. Use JUnit
to create tests. Call this method from onCreate()
:
private fun processDeepLink() {
if (intent?.data?.isHierarchical == true) {
val data = intent?.dataString
if (intent?.action == Intent.ACTION_VIEW && data != null) {
when {
REGEX.matches(data) -> // Get id and open some screen.
else -> // Either open MainActivity or skip this URL (open web browser instead).
}
finish()
}
}
}
companion object {
val REGEX = "^https://example.com/some_request/(\\d+).*".toRegex()
}
Warning! If you open a web browser from the application, you will fall into a loop. When clicking a link to your domain in the application, a browser won't appear, but your application will be opened automatically! What a surprise! So, in processDeepLink
you should check the URL and open MainActivity
when URL matches one of your masks. Skip others. Now a user will see a dialog with a list of browsers and your application (like in Deep Link). This happens because your application also handles links to your domains, like a browser.
You can also use WebView
instead of a browser (not a good solution), open Chrome Custom Tabs
or Chrome
.
Use device with Android 6 or later.
If https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls
returned no errors, build the application. Create an Email message, SMS, QR-code or another application with a link to your domain. Click it, and App Link will open your application or Deep Link will show a dialog to choose an application.
If App Link didn't work, read later.
In LogCat
select No Filters
and type IntentFilter
into search box. There should be:
I/IntentFilterIntentOp: Verifying IntentFilter. verificationId:2 scheme:"https" hosts:"example.com" package:"com.my_package".
I/IntentFilterIntentOp: Verification 0 complete. Success:true. Failed hosts:.
Probably you will get:
I/IntentFilterIntentOp: Verifying IntentFilter. verificationId:0 scheme:"https" hosts:"example.com server.com" package:"com.my_package".
I/IntentFilterIntentOp: Verification 0 complete. Success:false. Failed hosts:server.com.
Later you will try to fix domains in the application, so sometimes you can launch for clean install:
adb shell pm clear com.android.statementservice
Start adb shell dumpsys package d
and find your domains. There should be:
Package Name: com.my_package
Domains: example.com server.com
Status: always : 200000000
But probably it will be:
Package Name: com.my_package
Domains: example.com server.com
Status: ask
See also https://chris.orr.me.uk/android-app-linking-how-it-works/. Strange, but in an emulator it wrote: always
, while App Link didn't work.
I also tried adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://example.com"
to test App Link without a browser, but later it didn't work.
If you have several domains, comment (or remove) other domains in AndroidManifest
(retain only one domain, for example, "example.com"). Then click a URL https://example.com/something and check it uses App Link.
In my case I checked release
and debug
builds of the application. While the debug
build worked with App Link, release
didn't (and sometimes vise versa). I used the solution of rekire:
<meta-data
android:name="asset_statements"
android:resource="@string/asset_statements"/>
and it helped for 2 domains, but later stopped, so I removed it. In the end I wrote in AndroidManifest
<data>
tags with one attribute as j__m said.
Even if only one domain was failing, App Link was not working for other domains either. You can check domains one by one retaining only one domain each time in AndroidManifest
.
See also http://androidideas.com/handling-app-links-in-android/, https://willowtreeapps.com/ideas/a-better-user-experience-for-deep-linking-on-android, https://developer.android.com/training/app-links/verify-site-associations,
Upvotes: 15
Reputation: 1572
I'm sure this doesn't answer the original question, as I think it pre-dates Android App Bundles, but the thing that was ultimately causing a failure for me was that I'd enabled Google Play Console re-signing of the app (required for AABs) and therefore the SHA-256 fingerprint I was getting from keytool
did not match the digital signature of the downloaded app.
Updating my assetlinks.json
with the fingerprints from the console solved it.
Upvotes: 3
Reputation: 8284
In our case we had 2 intent filters with applinks in our manifest: one with autoVerify="true"
and one without.
So the verifier tried to verify domains for the 2nd intent filter and failed, and treated all our applinks as "not verified". You can find more details in this question.
You have to make sure every applink can be verified (which means adding assetlinks.json
for every domain to be verified).
Upvotes: 1
Reputation: 12181
Thanks for all the other answers here, I was able to find my issue. In spite of doing everything right. This was my problem.
How this can go wrong is simple. If a project has multiple autoverify urls, then the OS tries to verify it all. Even if one fails then the OS fails the verification of every URL.
Open the manifest file in your main app module, then choose the Merged Manifest option from the bottom tab. Now check the Manifest sources(list) on the right and manually look up manifest files of every library project.
In my case, a third-party library's auto verify flag was enabled. My two-day search comes to an end. Good luck to you.
Upvotes: 0
Reputation: 1345
For me, don't change anything of assetlinks.json
including trimming blanks and line-breaks.
Upvotes: 0
Reputation: 3734
For us it was Windows line endings!
Testing with "https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://domain1:port&relation=delegate_permission/common.handle_all_urls" proved invaluable as it gave us an "Could not parse statement list (not valid JSON)" error which led us to the problem.
TIP: It was good to use the 'Save File' button in the Android Studio App Links Assistant instead of copying and pasting as we did - that way it generates the file itself and is guaranteed not to have this issue.
Upvotes: 19
Reputation: 712
So I resolved my issue. Not sure which one did it (might have been a combination), but this is what I did:
When I first faced this issue, it was because my network was blocking calls to Google's servers for verifying the app links.
As OP and other answers have touched on, in general the API call to endpoint:
digitalassetlinks.googleapis.com
must be successful to bypass the chooser dialog. This is the web call the Android system makes, to verify the Digital Asset Link JSON file, and seems to be made upon app install/update. A helpful place to look is the Logcat, searching for items with the text "I/SingleHostAsyncVerifier:". If you see "--> true" at the end of the log, your app
Lately though, these calls have been failing for me due to what appears to be some bug which may have been introduced recently. The device is receiving this response from the API call above:
Error: unavailable: Wrong content type in HTTP response headers while fetching statements from {host}/.well-known/assetlinks.json (which is equivalent to '{host}/.well-known/assetlinks.json'): expected 'Content-Type: application/json' but found text/html [11] while fetching Web statements from {host}./.well-known/assetlinks.json
It's been awhile since I last looked at these requests, so I don't remember what they looked like before. But it seems possible that there may have been some recent update involving App Links or the Android networking framework, where they switched over to protocol buffers for this feature (and forgot to support it in another one).
Another indication that things may have changed, is that the request path today appears different from the ones mentioned in the previous answers:
https://digitalassetlinks.googleapis.com/google.digitalassetlinks.v1.AssetLinks/Check
Upvotes: 6
Reputation: 5154
For me, it was the fact that my assetlinks.json
file was UTF-8 and contained a byte-order mark (BOM), which is a three-byte magic number at the head of the file that signals the encoding to a consuming program. The BOM is optional, and apparently the Google / Android tools do not like to see it. When it was present, Google's digital asset links verifier (URL below) gave me a "malformed JSON" error.
If you're using Visual Studio, here's how to determine if you have the BOM in your file, and remove it if necessary:
EF BB BF
, that's the problem.Here's the URL you can use to check your file (replace example.com with your actual URL):
Upvotes: 6
Reputation: 47975
There are some common pitfalls which you should check twice (I don't say that you did it wrong. It is just a check list):
assetlinks.json
is valid and stored accessible from https://example.com/.well-known/assetlinks.json
to do that you need to visit https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site= https://example.com&relation=delegate_permission/common.handle_all_urls
, there must be no errors.<intent-filters>
which contain your <data>
tags have the attribute android:autoVerify="true"
.Verify that you have the required <meta-data>
tag in your <application>
tag:
<meta-data
android:name="asset_statements"
android:resource="@string/asset_statements"/>
The content of the asset_statements
string must be:
<string name="asset_statements" translatable="false">[{\"include\": \"https://example.com/.well-known/assetlinks.json\"}]
Use for debug also the release signing certificate (don't be scared you cannot upload it accidentally) use this in your build.gradle
:
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
debuggable true
signingConfig signingConfigs.release
}
}
Upvotes: 20
Reputation: 7002
System app selection window in two cases
1) User makes changes to settings related opening links by going to settings > apps > gear icon > opening links > select an app > open supported links > choose prompt every time.
2)Default app is not set by user and auto verify is not enabled in one of the app links supported app
I think in your case auto verify is enabled, so please check user settings.
Upvotes: 0
Reputation: 240
In my case, adb shell dumpsys package d
revealed that the packageName
was incorrectly configured in assetlinks.json
. I had used the package
attribute value of my manifest
tag in AndroidManifest.xml
, but I should have used the android.defaultConfig.packageId
value in my build.gradle
file.
Upvotes: 0
Reputation: 657
For me, it came down to checking all the basics:
Upvotes: 11