use PackageInfoCompat instead of dealing with signatures manually

This commit is contained in:
Ax333l 2024-08-12 22:35:52 +02:00
parent 3f497a93d0
commit 886ceaf4b0
No known key found for this signature in database
GPG key ID: D2B4D85271127D23
7 changed files with 56 additions and 65 deletions

View file

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "98837fd72fde0272894bce063c1095af",
"identityHash": "ab113134d89f2c5e412e87775510b327",
"entities": [
{
"tableName": "patch_bundles",
@ -405,7 +405,7 @@
},
{
"tableName": "trusted_downloader_plugins",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `signature` TEXT NOT NULL, PRIMARY KEY(`package_name`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `signature` BLOB NOT NULL, PRIMARY KEY(`package_name`))",
"fields": [
{
"fieldPath": "packageName",
@ -416,7 +416,7 @@
{
"fieldPath": "signature",
"columnName": "signature",
"affinity": "TEXT",
"affinity": "BLOB",
"notNull": true
}
],
@ -433,7 +433,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '98837fd72fde0272894bce063c1095af')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ab113134d89f2c5e412e87775510b327')"
]
}
}

View file

@ -5,7 +5,7 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "trusted_downloader_plugins")
data class TrustedDownloaderPlugin(
class TrustedDownloaderPlugin(
@PrimaryKey @ColumnInfo(name = "package_name") val packageName: String,
@ColumnInfo(name = "signature") val signature: String
@ColumnInfo(name = "signature") val signature: ByteArray
)

View file

@ -8,7 +8,7 @@ import androidx.room.Upsert
@Dao
interface TrustedDownloaderPluginDao {
@Query("SELECT signature FROM trusted_downloader_plugins WHERE package_name = :packageName")
suspend fun getTrustedSignature(packageName: String): String?
suspend fun getTrustedSignature(packageName: String): ByteArray?
@Upsert
suspend fun upsertTrust(plugin: TrustedDownloaderPlugin)

View file

@ -1,9 +1,8 @@
package app.revanced.manager.domain.repository
import android.content.Context
import android.app.Application
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.Signature
import android.util.Log
import app.revanced.manager.data.room.AppDatabase
import app.revanced.manager.data.room.plugins.TrustedDownloaderPlugin
@ -30,7 +29,7 @@ import java.lang.reflect.Modifier
class DownloaderPluginRepository(
private val pm: PM,
private val prefs: PreferencesManager,
private val context: Context,
private val app: Application,
db: AppDatabase
) {
private val trustDao = db.trustedDownloaderPluginDao()
@ -50,16 +49,14 @@ class DownloaderPluginRepository(
}
suspend fun reload() {
val pluginPackages =
val plugins =
withContext(Dispatchers.IO) {
pm.getPackagesWithFeature(
PLUGIN_FEATURE,
flags = packageFlags
)
pm.getPackagesWithFeature(PLUGIN_FEATURE)
.associate { it.packageName to loadPlugin(it.packageName) }
}
_pluginStates.value = pluginPackages.associate { it.packageName to loadPlugin(it) }
installedPluginPackageNames.value = pluginPackages.map { it.packageName }.toSet()
_pluginStates.value = plugins
installedPluginPackageNames.value = plugins.keys
val acknowledgedPlugins = acknowledgedDownloaderPlugins.get()
val uninstalledPlugins = acknowledgedPlugins subtract installedPluginPackageNames.value
@ -78,28 +75,22 @@ class DownloaderPluginRepository(
return plugin to app.unwrapWith(plugin)
}
private suspend fun loadPlugin(packageInfo: PackageInfo): DownloaderPluginState {
private suspend fun loadPlugin(packageName: String): DownloaderPluginState {
try {
if (!verify(packageInfo)) return DownloaderPluginState.Untrusted
if (!verify(packageName)) return DownloaderPluginState.Untrusted
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
Log.e(tag, "Got exception while verifying plugin ${packageInfo.packageName}", e)
Log.e(tag, "Got exception while verifying plugin $packageName", e)
return DownloaderPluginState.Failed(e)
}
val downloaderContext = DownloaderContext(
androidContext = context.createPackageContext(
packageInfo.packageName,
0
),
pluginHostPackageName = context.packageName
)
return try {
val className =
packageInfo.applicationInfo.metaData.getString(METADATA_PLUGIN_CLASS)
?: throw Exception("Missing metadata attribute $METADATA_PLUGIN_CLASS")
val packageInfo = pm.getPackageInfo(packageName, flags = PackageManager.GET_META_DATA)!!
val pluginContext = app.createPackageContext(packageName, 0)
val className = packageInfo.applicationInfo.metaData.getString(METADATA_PLUGIN_CLASS)
?: throw Exception("Missing metadata attribute $METADATA_PLUGIN_CLASS")
val classLoader = PathClassLoader(
packageInfo.applicationInfo.sourceDir,
Downloader::class.java.classLoader
@ -107,12 +98,17 @@ class DownloaderPluginRepository(
val downloader = classLoader
.loadClass(className)
.getDownloaderImplementation(downloaderContext)
.getDownloaderImplementation(
DownloaderContext(
androidContext = pluginContext,
pluginHostPackageName = app.packageName
)
)
@Suppress("UNCHECKED_CAST")
DownloaderPluginState.Loaded(
LoadedDownloaderPlugin(
packageInfo.packageName,
packageName,
with(pm) { packageInfo.label() },
packageInfo.versionName,
downloader.get,
@ -123,22 +119,22 @@ class DownloaderPluginRepository(
} catch (e: CancellationException) {
throw e
} catch (t: Throwable) {
Log.e(tag, "Failed to load plugin ${packageInfo.packageName}", t)
Log.e(tag, "Failed to load plugin $packageName", t)
DownloaderPluginState.Failed(t)
}
}
suspend fun trustPackage(packageInfo: PackageInfo) {
suspend fun trustPackage(packageName: String) {
trustDao.upsertTrust(
TrustedDownloaderPlugin(
packageInfo.packageName,
pm.getSignatures(packageInfo).first().toCharsString()
packageName,
pm.getSignature(packageName).toByteArray()
)
)
reload()
prefs.edit {
acknowledgedDownloaderPlugins += packageInfo.packageName
acknowledgedDownloaderPlugins += packageName
}
}
@ -148,19 +144,17 @@ class DownloaderPluginRepository(
suspend fun acknowledgeAllNewPlugins() =
acknowledgedDownloaderPlugins.update(installedPluginPackageNames.value)
private suspend fun verify(packageInfo: PackageInfo): Boolean {
private suspend fun verify(packageName: String): Boolean {
val expectedSignature =
trustDao.getTrustedSignature(packageInfo.packageName)?.let(::Signature) ?: return false
trustDao.getTrustedSignature(packageName) ?: return false
return expectedSignature in pm.getSignatures(packageInfo)
return pm.hasSignature(packageName, expectedSignature)
}
private companion object {
const val PLUGIN_FEATURE = "app.revanced.manager.plugin.downloader"
const val METADATA_PLUGIN_CLASS = "app.revanced.manager.plugin.downloader.class"
val packageFlags = PackageManager.GET_META_DATA or PM.signaturesFlag
val Class<*>.isDownloader get() = Downloader::class.java.isAssignableFrom(this)
const val PUBLIC_STATIC = Modifier.PUBLIC or Modifier.STATIC
val Int.isPublicStatic get() = (this and PUBLIC_STATIC) == PUBLIC_STATIC

View file

@ -111,16 +111,15 @@ fun DownloadsSettingsScreen(
val packageInfo =
remember(packageName) {
viewModel.pm.getPackageInfo(
packageName,
flags = PM.signaturesFlag
packageName
)
} ?: return@item
if (showDialog) {
val signature =
remember(packageInfo) {
remember(packageName) {
val androidSignature =
viewModel.pm.getSignatures(packageInfo).first()
viewModel.pm.getSignature(packageName)
val hash = MessageDigest.getInstance("SHA-256")
.digest(androidSignature.toByteArray())
hash.toHexString(format = HexFormat.UpperCase)
@ -157,7 +156,7 @@ fun DownloadsSettingsScreen(
),
onDismiss = ::dismiss,
onConfirm = {
viewModel.trustPlugin(packageInfo)
viewModel.trustPlugin(packageName)
dismiss()
}
)

View file

@ -58,8 +58,8 @@ class DownloadsViewModel(
isRefreshingPlugins = false
}
fun trustPlugin(packageInfo: PackageInfo) = viewModelScope.launch {
downloaderPluginRepository.trustPackage(packageInfo)
fun trustPlugin(packageName: String) = viewModelScope.launch {
downloaderPluginRepository.trustPackage(packageName)
}
fun revokePluginTrust(packageName: String) = viewModelScope.launch {

View file

@ -14,6 +14,7 @@ import android.content.pm.Signature
import android.os.Build
import android.os.Parcelable
import androidx.compose.runtime.Immutable
import androidx.core.content.pm.PackageInfoCompat
import app.revanced.manager.domain.repository.PatchBundleRepository
import app.revanced.manager.service.InstallService
import app.revanced.manager.service.UninstallService
@ -37,7 +38,6 @@ data class AppInfo(
) : Parcelable
@SuppressLint("QueryPermissionsNeeded")
@Suppress("Deprecation")
class PM(
private val app: Application,
patchBundleRepository: PatchBundleRepository
@ -100,8 +100,8 @@ class PM(
else
app.packageManager.getInstalledPackages(flags)
fun getPackagesWithFeature(feature: String, flags: Int = 0) =
getInstalledPackages(PackageManager.GET_CONFIGURATIONS or flags)
fun getPackagesWithFeature(feature: String) =
getInstalledPackages(PackageManager.GET_CONFIGURATIONS)
.filter { pkg ->
pkg.reqFeatures?.any { it.name == feature } ?: false
}
@ -129,15 +129,17 @@ class PM(
return pkgInfo
}
fun getSignatures(packageInfo: PackageInfo): Array<Signature> {
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
packageInfo.signingInfo.apkContentsSigners
else packageInfo.signatures
fun getSignature(packageName: String): Signature =
// Get the last signature from the list because we want the newest one if SigningInfo.getSigningCertificateHistory() was used.
PackageInfoCompat.getSignatures(app.packageManager, packageName).last()
if (signatures.isEmpty()) throw Exception("Signature information was not queried")
return signatures
}
@SuppressLint("InlinedApi")
fun hasSignature(packageName: String, signature: ByteArray) = PackageInfoCompat.hasSignatures(
app.packageManager,
packageName,
mapOf(signature to PackageManager.CERT_INPUT_RAW_X509),
false
)
fun PackageInfo.label() = this.applicationInfo.loadLabel(app.packageManager).toString()
@ -196,8 +198,4 @@ class PM(
Intent(this, UninstallService::class.java),
intentFlags
).intentSender
companion object {
val signaturesFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) PackageManager.GET_SIGNING_CERTIFICATES else PackageManager.GET_SIGNATURES
}
}