I've been tearing my hair out on this one...
Fountain supports CarPlay and AndroidAuto so that you can browse your library and listen to podcasts from the car specific interface.
However GrapheneOS doesn't support AndroidAuto and as such the app crashes on GrapheneOS if we try to interface with certain AndroidAuto APIs. Specifically we are seeing this error:

java.lang.RuntimeException: Unable to bind to service fm.fountain.audio.service.MusicService@2d239f6 with Intent { cmp=fm.fountain.apps/fm.fountain.audio.service.MusicService }: java.lang.SecurityException: Failed to find provider androidx.car.app.connection for user 0; expected to find a valid ContentProvider for this authority

I'm looking for a simple way to detect in Kotlin that the Android device is running GrapheneOS (or any other solution to the issue)

250k sats for anyone who can help us figure this out.
Please share with any Android devs you know that might have experience in this area! 🙏
250,000 sats bounty
MerryOscar's bounties
This wont likely be insightful either, I have not developed an Android app but GrapheneOS developers have commented on it a couple of times, like previous comments you're probably better off trying to make a workaround that doesn't call the Android Auto API's on their own?
There are some methods here but they all have some disadvantages:
  1. The safest method is you could use Play Integrity API to do hardware-based attestation. This can detect what is a stock Android on an OEM and aftermarket operating system like GrapheneOS as they don't choose to spoof these checks. GrapheneOS passes MEETS_BASIC_INTEGRITY but not MEETS_DEVICE_INTEGRITY hence why Google Pay and some banking apps does not work. Maybe it's worth trying? https://developer.android.com/google/play/integrity/verdict#device-integrity-field
You can also add GrapheneOS' keys to the app for that: https://grapheneos.org/articles/attestation-compatibility-guide
  1. Apps can access list of installed apps on the profile if they are installed and the app is given the QUERY_ALL_PACKAGES permission, which Google docs call high-risk and has a lot of guidelines on how to use. I don't even think an app like yours could meet the strict requirements anyhow, seems to be just for podcasts?
If you somehow did meet requirements, maybe if you have an OS system app like app.attestation.auditor (GrapheneOS' Auditor system app) detected you can turn that feature off? GrapheneOS' Auditor for other devices uses the App IP app.attestation.auditor.play so conflicts wouldn't be an issue.
Hope you get some luck with this. GrapheneOS also has a matrix channel to talk to, maybe their development channel would be a good place to ask further questions?
reply
This might be very unhelpful but if this error only occurs on GrapheneOS can't you just try-catch it and call a fallback solution from there?
reply
yes we tried this but strangely putting the override fun onCarConnected() {} function in a try block seems to stop the listener from firing in the case where the app is open before connecting to the car - very odd!
reply
Update - we solved it with this "gearhead" package check:

fun isAndroidAutoAvailable(): Boolean { val packageManager = this.packageManager val packages = packageManager.getInstalledPackages(PackageManager.GET_SERVICES) for (packageInfo in packages) { packageInfo.services?.forEach { serviceInfo -> val serviceName = serviceInfo.name if (serviceName.contains("gearhead")) { return true } } } return false }

I'm going to pay out some of the bounty to those that commented as it definitely helped us get to the solution - thanks guys! :)
reply
reply
I can understand your frustration with the issue. To detect if the Android device is running GrapheneOS programmatically in Kotlin, you can try checking the "ro.build.version.release" system property. GrapheneOS usually includes the term "GrapheneOS" in the build version release.
Here's a Kotlin function that checks for GrapheneOS:
fun isGrapheneOS(): Boolean { val buildVersion = System.getProperty("ro.build.version.release") return buildVersion.contains("GrapheneOS", ignoreCase = true) }
You can use this function to conditionally handle the AndroidAuto APIs based on whether the device is running GrapheneOS or not.
Please note that relying solely on the build version release might not be completely foolproof as manufacturers can customize this information. However, it can be a good starting point to identify GrapheneOS in most cases.
I hope this helps you resolve the issue. Good luck! 🍀
reply
Looks like there's some interesting suggestions already but another alternative is to ship a version to fdroid or something that doesn't have android auto. You're more than likely going to have graphene users check there first.
reply
Play with bluetooth, play via smart phone.
reply
deleted by author