diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarLauncher.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarLauncher.kt index a5fc8a1..217c212 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarLauncher.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarLauncher.kt @@ -239,6 +239,7 @@ internal fun launchEntry(entry: Entry, navigator: CarNavigator, frame: CarEnviro /** The app launcher: every app as an icon grid, grouped Phone, Maps, Music, Messaging, Web, Screen sharing, Settings. */ @Composable fun CarLauncher() { + val context = LocalContext.current val navigator = LocalCarNavigator.current val frame by CarEnvironment.frame.collectAsState() @@ -254,7 +255,7 @@ fun CarLauncher() { val id = entry.componentId return Tile( entry.label, entry.icon, entry.builtIn?.glyph, - broken = pkg != null && (KnownApps.isBroken(pkg) || (entry.template != null && KnownApps.screenBroken(pkg))), + broken = pkg != null && (KnownApps.isBroken(context, pkg) || (entry.template != null && KnownApps.screenBroken(pkg))), onLongClick = id?.let { { navigator.showAppMenu(it) } }, ) { openApp(entry, id?.let { running[it] }, navigator, frame) } } diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarServices.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarServices.kt index 47f084d..bbde9df 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarServices.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarServices.kt @@ -178,7 +178,7 @@ object CarServices { _hasNavApps.value = navApps.isNotEmpty() // Never auto-pick one already known to fail outright - a driver who plugs in should // land on a map, not on the one tile marked with a red X. - val candidates = navApps.filterNot { KnownApps.isBroken(it.component.packageName) } + val candidates = navApps.filterNot { KnownApps.isBroken(ctx, it.component.packageName) } val last = CarSettings.lastNav.value // The one used last, or - first run, nothing on record yet - whichever comes first: // a driver who plugs in for the first time shouldn't have to go pick one by hand diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/KnownApps.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/KnownApps.kt index 74289ee..cdc4468 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/KnownApps.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/KnownApps.kt @@ -1,5 +1,9 @@ package app.seb3thehacker.gearslip.host +import android.content.Context +import android.content.pm.PackageManager +import java.security.MessageDigest + /** * Apps that have been run through Gearslip from start to finish and behave. The launcher marks * them so a driver can pick something that works without finding out at the wheel. @@ -65,7 +69,30 @@ object KnownApps { "com.generalmagic.magicearth", // Magic Earth: never gets past connecting ) - fun isBroken(packageName: String): Boolean = packageName in broken + fun isBroken(context: Context, packageName: String): Boolean = + packageName in broken && !patched(context, packageName) + + /** + * The certificate each broken app's own publisher signs it with. A copy signed with another + * key has been patched to accept Gearslip, like Waze with the waze-gearslip Morphe patch, and + * works. + */ + private val officialSigners = mapOf( + "com.waze" to "03637f6c5d8f604e6fdb79a6ffbfa578de4e318f8da22fc6106665247f8807d7", + ) + + /** Whether the installed copy is signed by someone other than its publisher. */ + private fun patched(context: Context, packageName: String): Boolean { + val official = officialSigners[packageName] ?: return false + val signers = runCatching { + context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES) + .signingInfo?.apkContentsSigners + }.getOrNull() ?: return false + val sha256 = MessageDigest.getInstance("SHA-256") + return signers.isNotEmpty() && signers.none { cert -> + sha256.digest(cert.toByteArray()).joinToString("") { "%02x".format(it) } == official + } + } /** * Shown on the launcher with a yellow mark: starts and can be used, but not cleanly enough to