mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-05-24 02:22:10 +02:00
use PackageInfoCompat instead of dealing with signatures manually
This commit is contained in:
parent
3f497a93d0
commit
886ceaf4b0
@ -2,7 +2,7 @@
|
|||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"identityHash": "98837fd72fde0272894bce063c1095af",
|
"identityHash": "ab113134d89f2c5e412e87775510b327",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "patch_bundles",
|
"tableName": "patch_bundles",
|
||||||
@ -405,7 +405,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "trusted_downloader_plugins",
|
"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": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "packageName",
|
"fieldPath": "packageName",
|
||||||
@ -416,7 +416,7 @@
|
|||||||
{
|
{
|
||||||
"fieldPath": "signature",
|
"fieldPath": "signature",
|
||||||
"columnName": "signature",
|
"columnName": "signature",
|
||||||
"affinity": "TEXT",
|
"affinity": "BLOB",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -433,7 +433,7 @@
|
|||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"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
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "trusted_downloader_plugins")
|
@Entity(tableName = "trusted_downloader_plugins")
|
||||||
data class TrustedDownloaderPlugin(
|
class TrustedDownloaderPlugin(
|
||||||
@PrimaryKey @ColumnInfo(name = "package_name") val packageName: String,
|
@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
|
@Dao
|
||||||
interface TrustedDownloaderPluginDao {
|
interface TrustedDownloaderPluginDao {
|
||||||
@Query("SELECT signature FROM trusted_downloader_plugins WHERE package_name = :packageName")
|
@Query("SELECT signature FROM trusted_downloader_plugins WHERE package_name = :packageName")
|
||||||
suspend fun getTrustedSignature(packageName: String): String?
|
suspend fun getTrustedSignature(packageName: String): ByteArray?
|
||||||
|
|
||||||
@Upsert
|
@Upsert
|
||||||
suspend fun upsertTrust(plugin: TrustedDownloaderPlugin)
|
suspend fun upsertTrust(plugin: TrustedDownloaderPlugin)
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package app.revanced.manager.domain.repository
|
package app.revanced.manager.domain.repository
|
||||||
|
|
||||||
import android.content.Context
|
import android.app.Application
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.Signature
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import app.revanced.manager.data.room.AppDatabase
|
import app.revanced.manager.data.room.AppDatabase
|
||||||
import app.revanced.manager.data.room.plugins.TrustedDownloaderPlugin
|
import app.revanced.manager.data.room.plugins.TrustedDownloaderPlugin
|
||||||
@ -30,7 +29,7 @@ import java.lang.reflect.Modifier
|
|||||||
class DownloaderPluginRepository(
|
class DownloaderPluginRepository(
|
||||||
private val pm: PM,
|
private val pm: PM,
|
||||||
private val prefs: PreferencesManager,
|
private val prefs: PreferencesManager,
|
||||||
private val context: Context,
|
private val app: Application,
|
||||||
db: AppDatabase
|
db: AppDatabase
|
||||||
) {
|
) {
|
||||||
private val trustDao = db.trustedDownloaderPluginDao()
|
private val trustDao = db.trustedDownloaderPluginDao()
|
||||||
@ -50,16 +49,14 @@ class DownloaderPluginRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun reload() {
|
suspend fun reload() {
|
||||||
val pluginPackages =
|
val plugins =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
pm.getPackagesWithFeature(
|
pm.getPackagesWithFeature(PLUGIN_FEATURE)
|
||||||
PLUGIN_FEATURE,
|
.associate { it.packageName to loadPlugin(it.packageName) }
|
||||||
flags = packageFlags
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_pluginStates.value = pluginPackages.associate { it.packageName to loadPlugin(it) }
|
_pluginStates.value = plugins
|
||||||
installedPluginPackageNames.value = pluginPackages.map { it.packageName }.toSet()
|
installedPluginPackageNames.value = plugins.keys
|
||||||
|
|
||||||
val acknowledgedPlugins = acknowledgedDownloaderPlugins.get()
|
val acknowledgedPlugins = acknowledgedDownloaderPlugins.get()
|
||||||
val uninstalledPlugins = acknowledgedPlugins subtract installedPluginPackageNames.value
|
val uninstalledPlugins = acknowledgedPlugins subtract installedPluginPackageNames.value
|
||||||
@ -78,28 +75,22 @@ class DownloaderPluginRepository(
|
|||||||
return plugin to app.unwrapWith(plugin)
|
return plugin to app.unwrapWith(plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadPlugin(packageInfo: PackageInfo): DownloaderPluginState {
|
private suspend fun loadPlugin(packageName: String): DownloaderPluginState {
|
||||||
try {
|
try {
|
||||||
if (!verify(packageInfo)) return DownloaderPluginState.Untrusted
|
if (!verify(packageName)) return DownloaderPluginState.Untrusted
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
throw e
|
throw e
|
||||||
} catch (e: Exception) {
|
} 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)
|
return DownloaderPluginState.Failed(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val downloaderContext = DownloaderContext(
|
|
||||||
androidContext = context.createPackageContext(
|
|
||||||
packageInfo.packageName,
|
|
||||||
0
|
|
||||||
),
|
|
||||||
pluginHostPackageName = context.packageName
|
|
||||||
)
|
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val className =
|
val packageInfo = pm.getPackageInfo(packageName, flags = PackageManager.GET_META_DATA)!!
|
||||||
packageInfo.applicationInfo.metaData.getString(METADATA_PLUGIN_CLASS)
|
val pluginContext = app.createPackageContext(packageName, 0)
|
||||||
?: throw Exception("Missing metadata attribute $METADATA_PLUGIN_CLASS")
|
|
||||||
|
val className = packageInfo.applicationInfo.metaData.getString(METADATA_PLUGIN_CLASS)
|
||||||
|
?: throw Exception("Missing metadata attribute $METADATA_PLUGIN_CLASS")
|
||||||
val classLoader = PathClassLoader(
|
val classLoader = PathClassLoader(
|
||||||
packageInfo.applicationInfo.sourceDir,
|
packageInfo.applicationInfo.sourceDir,
|
||||||
Downloader::class.java.classLoader
|
Downloader::class.java.classLoader
|
||||||
@ -107,12 +98,17 @@ class DownloaderPluginRepository(
|
|||||||
|
|
||||||
val downloader = classLoader
|
val downloader = classLoader
|
||||||
.loadClass(className)
|
.loadClass(className)
|
||||||
.getDownloaderImplementation(downloaderContext)
|
.getDownloaderImplementation(
|
||||||
|
DownloaderContext(
|
||||||
|
androidContext = pluginContext,
|
||||||
|
pluginHostPackageName = app.packageName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
DownloaderPluginState.Loaded(
|
DownloaderPluginState.Loaded(
|
||||||
LoadedDownloaderPlugin(
|
LoadedDownloaderPlugin(
|
||||||
packageInfo.packageName,
|
packageName,
|
||||||
with(pm) { packageInfo.label() },
|
with(pm) { packageInfo.label() },
|
||||||
packageInfo.versionName,
|
packageInfo.versionName,
|
||||||
downloader.get,
|
downloader.get,
|
||||||
@ -123,22 +119,22 @@ class DownloaderPluginRepository(
|
|||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
throw e
|
throw e
|
||||||
} catch (t: Throwable) {
|
} 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)
|
DownloaderPluginState.Failed(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun trustPackage(packageInfo: PackageInfo) {
|
suspend fun trustPackage(packageName: String) {
|
||||||
trustDao.upsertTrust(
|
trustDao.upsertTrust(
|
||||||
TrustedDownloaderPlugin(
|
TrustedDownloaderPlugin(
|
||||||
packageInfo.packageName,
|
packageName,
|
||||||
pm.getSignatures(packageInfo).first().toCharsString()
|
pm.getSignature(packageName).toByteArray()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
reload()
|
reload()
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
acknowledgedDownloaderPlugins += packageInfo.packageName
|
acknowledgedDownloaderPlugins += packageName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,19 +144,17 @@ class DownloaderPluginRepository(
|
|||||||
suspend fun acknowledgeAllNewPlugins() =
|
suspend fun acknowledgeAllNewPlugins() =
|
||||||
acknowledgedDownloaderPlugins.update(installedPluginPackageNames.value)
|
acknowledgedDownloaderPlugins.update(installedPluginPackageNames.value)
|
||||||
|
|
||||||
private suspend fun verify(packageInfo: PackageInfo): Boolean {
|
private suspend fun verify(packageName: String): Boolean {
|
||||||
val expectedSignature =
|
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 {
|
private companion object {
|
||||||
const val PLUGIN_FEATURE = "app.revanced.manager.plugin.downloader"
|
const val PLUGIN_FEATURE = "app.revanced.manager.plugin.downloader"
|
||||||
const val METADATA_PLUGIN_CLASS = "app.revanced.manager.plugin.downloader.class"
|
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)
|
val Class<*>.isDownloader get() = Downloader::class.java.isAssignableFrom(this)
|
||||||
const val PUBLIC_STATIC = Modifier.PUBLIC or Modifier.STATIC
|
const val PUBLIC_STATIC = Modifier.PUBLIC or Modifier.STATIC
|
||||||
val Int.isPublicStatic get() = (this and PUBLIC_STATIC) == PUBLIC_STATIC
|
val Int.isPublicStatic get() = (this and PUBLIC_STATIC) == PUBLIC_STATIC
|
||||||
|
@ -111,16 +111,15 @@ fun DownloadsSettingsScreen(
|
|||||||
val packageInfo =
|
val packageInfo =
|
||||||
remember(packageName) {
|
remember(packageName) {
|
||||||
viewModel.pm.getPackageInfo(
|
viewModel.pm.getPackageInfo(
|
||||||
packageName,
|
packageName
|
||||||
flags = PM.signaturesFlag
|
|
||||||
)
|
)
|
||||||
} ?: return@item
|
} ?: return@item
|
||||||
|
|
||||||
if (showDialog) {
|
if (showDialog) {
|
||||||
val signature =
|
val signature =
|
||||||
remember(packageInfo) {
|
remember(packageName) {
|
||||||
val androidSignature =
|
val androidSignature =
|
||||||
viewModel.pm.getSignatures(packageInfo).first()
|
viewModel.pm.getSignature(packageName)
|
||||||
val hash = MessageDigest.getInstance("SHA-256")
|
val hash = MessageDigest.getInstance("SHA-256")
|
||||||
.digest(androidSignature.toByteArray())
|
.digest(androidSignature.toByteArray())
|
||||||
hash.toHexString(format = HexFormat.UpperCase)
|
hash.toHexString(format = HexFormat.UpperCase)
|
||||||
@ -157,7 +156,7 @@ fun DownloadsSettingsScreen(
|
|||||||
),
|
),
|
||||||
onDismiss = ::dismiss,
|
onDismiss = ::dismiss,
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
viewModel.trustPlugin(packageInfo)
|
viewModel.trustPlugin(packageName)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -58,8 +58,8 @@ class DownloadsViewModel(
|
|||||||
isRefreshingPlugins = false
|
isRefreshingPlugins = false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trustPlugin(packageInfo: PackageInfo) = viewModelScope.launch {
|
fun trustPlugin(packageName: String) = viewModelScope.launch {
|
||||||
downloaderPluginRepository.trustPackage(packageInfo)
|
downloaderPluginRepository.trustPackage(packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun revokePluginTrust(packageName: String) = viewModelScope.launch {
|
fun revokePluginTrust(packageName: String) = viewModelScope.launch {
|
||||||
|
@ -14,6 +14,7 @@ import android.content.pm.Signature
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
import app.revanced.manager.domain.repository.PatchBundleRepository
|
import app.revanced.manager.domain.repository.PatchBundleRepository
|
||||||
import app.revanced.manager.service.InstallService
|
import app.revanced.manager.service.InstallService
|
||||||
import app.revanced.manager.service.UninstallService
|
import app.revanced.manager.service.UninstallService
|
||||||
@ -37,7 +38,6 @@ data class AppInfo(
|
|||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@SuppressLint("QueryPermissionsNeeded")
|
@SuppressLint("QueryPermissionsNeeded")
|
||||||
@Suppress("Deprecation")
|
|
||||||
class PM(
|
class PM(
|
||||||
private val app: Application,
|
private val app: Application,
|
||||||
patchBundleRepository: PatchBundleRepository
|
patchBundleRepository: PatchBundleRepository
|
||||||
@ -100,8 +100,8 @@ class PM(
|
|||||||
else
|
else
|
||||||
app.packageManager.getInstalledPackages(flags)
|
app.packageManager.getInstalledPackages(flags)
|
||||||
|
|
||||||
fun getPackagesWithFeature(feature: String, flags: Int = 0) =
|
fun getPackagesWithFeature(feature: String) =
|
||||||
getInstalledPackages(PackageManager.GET_CONFIGURATIONS or flags)
|
getInstalledPackages(PackageManager.GET_CONFIGURATIONS)
|
||||||
.filter { pkg ->
|
.filter { pkg ->
|
||||||
pkg.reqFeatures?.any { it.name == feature } ?: false
|
pkg.reqFeatures?.any { it.name == feature } ?: false
|
||||||
}
|
}
|
||||||
@ -129,15 +129,17 @@ class PM(
|
|||||||
return pkgInfo
|
return pkgInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSignatures(packageInfo: PackageInfo): Array<Signature> {
|
fun getSignature(packageName: String): Signature =
|
||||||
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
// Get the last signature from the list because we want the newest one if SigningInfo.getSigningCertificateHistory() was used.
|
||||||
packageInfo.signingInfo.apkContentsSigners
|
PackageInfoCompat.getSignatures(app.packageManager, packageName).last()
|
||||||
else packageInfo.signatures
|
|
||||||
|
|
||||||
if (signatures.isEmpty()) throw Exception("Signature information was not queried")
|
@SuppressLint("InlinedApi")
|
||||||
|
fun hasSignature(packageName: String, signature: ByteArray) = PackageInfoCompat.hasSignatures(
|
||||||
return signatures
|
app.packageManager,
|
||||||
}
|
packageName,
|
||||||
|
mapOf(signature to PackageManager.CERT_INPUT_RAW_X509),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
fun PackageInfo.label() = this.applicationInfo.loadLabel(app.packageManager).toString()
|
fun PackageInfo.label() = this.applicationInfo.loadLabel(app.packageManager).toString()
|
||||||
|
|
||||||
@ -196,8 +198,4 @@ class PM(
|
|||||||
Intent(this, UninstallService::class.java),
|
Intent(this, UninstallService::class.java),
|
||||||
intentFlags
|
intentFlags
|
||||||
).intentSender
|
).intentSender
|
||||||
|
|
||||||
companion object {
|
|
||||||
val signaturesFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) PackageManager.GET_SIGNING_CERTIFICATES else PackageManager.GET_SIGNATURES
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user