mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-12 18:04:28 +01:00
use PackageInfoCompat instead of dealing with signatures manually
This commit is contained in:
parent
3f497a93d0
commit
886ceaf4b0
7 changed files with 56 additions and 65 deletions
|
@ -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')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue