Store app name and whether it is a launchable system app in metadata
this is helpful for restore, so the user can see app names when deciding which apps to restore
This commit is contained in:
parent
5a2f1187a8
commit
56d8d64261
11 changed files with 117 additions and 22 deletions
|
@ -7,6 +7,7 @@
|
||||||
<w>ejectable</w>
|
<w>ejectable</w>
|
||||||
<w>hasher</w>
|
<w>hasher</w>
|
||||||
<w>hkdf</w>
|
<w>hkdf</w>
|
||||||
|
<w>launchable</w>
|
||||||
<w>restorable</w>
|
<w>restorable</w>
|
||||||
<w>seedvault</w>
|
<w>seedvault</w>
|
||||||
<w>snowden</w>
|
<w>snowden</w>
|
||||||
|
|
|
@ -26,7 +26,7 @@ data class BackupMetadata(
|
||||||
internal var d2dBackup: Boolean = false,
|
internal var d2dBackup: Boolean = false,
|
||||||
internal val packageMetadataMap: PackageMetadataMap = PackageMetadataMap(),
|
internal val packageMetadataMap: PackageMetadataMap = PackageMetadataMap(),
|
||||||
) {
|
) {
|
||||||
val size: Long?
|
val size: Long
|
||||||
get() = packageMetadataMap.values.sumOf { m ->
|
get() = packageMetadataMap.values.sumOf { m ->
|
||||||
(m.size ?: 0L) + (m.splits?.sumOf { it.size ?: 0L } ?: 0L)
|
(m.size ?: 0L) + (m.splits?.sumOf { it.size ?: 0L } ?: 0L)
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,9 @@ data class PackageMetadata(
|
||||||
internal var state: PackageState = UNKNOWN_ERROR,
|
internal var state: PackageState = UNKNOWN_ERROR,
|
||||||
internal var backupType: BackupType? = null,
|
internal var backupType: BackupType? = null,
|
||||||
internal var size: Long? = null,
|
internal var size: Long? = null,
|
||||||
|
internal var name: CharSequence? = null,
|
||||||
internal val system: Boolean = false,
|
internal val system: Boolean = false,
|
||||||
|
internal val isLaunchableSystemApp: Boolean = false,
|
||||||
internal val version: Long? = null,
|
internal val version: Long? = null,
|
||||||
internal val installer: String? = null,
|
internal val installer: String? = null,
|
||||||
internal val splits: List<ApkSplit>? = null,
|
internal val splits: List<ApkSplit>? = null,
|
||||||
|
@ -110,7 +112,9 @@ internal const val JSON_PACKAGE_TIME = "time"
|
||||||
internal const val JSON_PACKAGE_BACKUP_TYPE = "backupType"
|
internal const val JSON_PACKAGE_BACKUP_TYPE = "backupType"
|
||||||
internal const val JSON_PACKAGE_STATE = "state"
|
internal const val JSON_PACKAGE_STATE = "state"
|
||||||
internal const val JSON_PACKAGE_SIZE = "size"
|
internal const val JSON_PACKAGE_SIZE = "size"
|
||||||
|
internal const val JSON_PACKAGE_APP_NAME = "name"
|
||||||
internal const val JSON_PACKAGE_SYSTEM = "system"
|
internal const val JSON_PACKAGE_SYSTEM = "system"
|
||||||
|
internal const val JSON_PACKAGE_SYSTEM_LAUNCHER = "systemLauncher"
|
||||||
internal const val JSON_PACKAGE_VERSION = "version"
|
internal const val JSON_PACKAGE_VERSION = "version"
|
||||||
internal const val JSON_PACKAGE_INSTALLER = "installer"
|
internal const val JSON_PACKAGE_INSTALLER = "installer"
|
||||||
internal const val JSON_PACKAGE_SPLITS = "splits"
|
internal const val JSON_PACKAGE_SPLITS = "splits"
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.stevesoltys.seedvault.encodeBase64
|
||||||
import com.stevesoltys.seedvault.header.VERSION
|
import com.stevesoltys.seedvault.header.VERSION
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.APK_AND_DATA
|
import com.stevesoltys.seedvault.metadata.PackageState.APK_AND_DATA
|
||||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
|
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||||
import com.stevesoltys.seedvault.transport.backup.isSystemApp
|
import com.stevesoltys.seedvault.transport.backup.isSystemApp
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -41,6 +42,7 @@ internal class MetadataManager(
|
||||||
private val crypto: Crypto,
|
private val crypto: Crypto,
|
||||||
private val metadataWriter: MetadataWriter,
|
private val metadataWriter: MetadataWriter,
|
||||||
private val metadataReader: MetadataReader,
|
private val metadataReader: MetadataReader,
|
||||||
|
private val packageService: PackageService,
|
||||||
private val settingsManager: SettingsManager,
|
private val settingsManager: SettingsManager,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -63,7 +65,11 @@ internal class MetadataManager(
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
val backupSize: Long? get() = metadata.size
|
val backupSize: Long get() = metadata.size
|
||||||
|
|
||||||
|
private val launchableSystemApps by lazy {
|
||||||
|
packageService.launchableSystemApps.map { it.activityInfo.packageName }.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this when initializing a new device.
|
* Call this when initializing a new device.
|
||||||
|
@ -111,8 +117,11 @@ internal class MetadataManager(
|
||||||
val oldPackageMetadata = metadata.packageMetadataMap[packageName]
|
val oldPackageMetadata = metadata.packageMetadataMap[packageName]
|
||||||
?: PackageMetadata()
|
?: PackageMetadata()
|
||||||
modifyCachedMetadata {
|
modifyCachedMetadata {
|
||||||
|
val isSystemApp = packageInfo.isSystemApp()
|
||||||
metadata.packageMetadataMap[packageName] = oldPackageMetadata.copy(
|
metadata.packageMetadataMap[packageName] = oldPackageMetadata.copy(
|
||||||
system = packageInfo.isSystemApp(),
|
name = packageInfo.applicationInfo?.loadLabel(context.packageManager),
|
||||||
|
system = isSystemApp,
|
||||||
|
isLaunchableSystemApp = isSystemApp && launchableSystemApps.contains(packageName),
|
||||||
version = packageMetadata.version,
|
version = packageMetadata.version,
|
||||||
installer = packageMetadata.installer,
|
installer = packageMetadata.installer,
|
||||||
splits = packageMetadata.splits,
|
splits = packageMetadata.splits,
|
||||||
|
@ -144,12 +153,16 @@ internal class MetadataManager(
|
||||||
metadata.time = now
|
metadata.time = now
|
||||||
metadata.d2dBackup = settingsManager.d2dBackupsEnabled()
|
metadata.d2dBackup = settingsManager.d2dBackupsEnabled()
|
||||||
metadata.packageMetadataMap.getOrPut(packageName) {
|
metadata.packageMetadataMap.getOrPut(packageName) {
|
||||||
|
val isSystemApp = packageInfo.isSystemApp()
|
||||||
PackageMetadata(
|
PackageMetadata(
|
||||||
time = now,
|
time = now,
|
||||||
state = APK_AND_DATA,
|
state = APK_AND_DATA,
|
||||||
backupType = type,
|
backupType = type,
|
||||||
size = size,
|
size = size,
|
||||||
system = packageInfo.isSystemApp(),
|
name = packageInfo.applicationInfo?.loadLabel(context.packageManager),
|
||||||
|
system = isSystemApp,
|
||||||
|
isLaunchableSystemApp = isSystemApp &&
|
||||||
|
launchableSystemApps.contains(packageName),
|
||||||
)
|
)
|
||||||
}.apply {
|
}.apply {
|
||||||
time = now
|
time = now
|
||||||
|
@ -157,6 +170,10 @@ internal class MetadataManager(
|
||||||
backupType = type
|
backupType = type
|
||||||
// don't override a previous K/V size, if there were no K/V changes
|
// don't override a previous K/V size, if there were no K/V changes
|
||||||
if (size != null) this.size = size
|
if (size != null) this.size = size
|
||||||
|
// update name, if none was set, yet (can happen while migrating to storing names)
|
||||||
|
if (this.name == null) {
|
||||||
|
this.name = packageInfo.applicationInfo?.loadLabel(context.packageManager)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,11 +195,15 @@ internal class MetadataManager(
|
||||||
check(packageState != APK_AND_DATA) { "Backup Error with non-error package state." }
|
check(packageState != APK_AND_DATA) { "Backup Error with non-error package state." }
|
||||||
modifyMetadata(metadataOutputStream) {
|
modifyMetadata(metadataOutputStream) {
|
||||||
metadata.packageMetadataMap.getOrPut(packageInfo.packageName) {
|
metadata.packageMetadataMap.getOrPut(packageInfo.packageName) {
|
||||||
|
val isSystemApp = packageInfo.isSystemApp()
|
||||||
PackageMetadata(
|
PackageMetadata(
|
||||||
time = 0L,
|
time = 0L,
|
||||||
state = packageState,
|
state = packageState,
|
||||||
backupType = backupType,
|
backupType = backupType,
|
||||||
system = packageInfo.isSystemApp()
|
name = packageInfo.applicationInfo?.loadLabel(context.packageManager),
|
||||||
|
system = isSystemApp,
|
||||||
|
isLaunchableSystemApp = isSystemApp &&
|
||||||
|
launchableSystemApps.contains(packageInfo.packageName),
|
||||||
)
|
)
|
||||||
}.state = packageState
|
}.state = packageState
|
||||||
}
|
}
|
||||||
|
@ -201,12 +222,22 @@ internal class MetadataManager(
|
||||||
packageState: PackageState,
|
packageState: PackageState,
|
||||||
) = modifyCachedMetadata {
|
) = modifyCachedMetadata {
|
||||||
metadata.packageMetadataMap.getOrPut(packageInfo.packageName) {
|
metadata.packageMetadataMap.getOrPut(packageInfo.packageName) {
|
||||||
|
val isSystemApp = packageInfo.isSystemApp()
|
||||||
PackageMetadata(
|
PackageMetadata(
|
||||||
time = 0L,
|
time = 0L,
|
||||||
state = packageState,
|
state = packageState,
|
||||||
system = packageInfo.isSystemApp(),
|
name = packageInfo.applicationInfo?.loadLabel(context.packageManager),
|
||||||
|
system = isSystemApp,
|
||||||
|
isLaunchableSystemApp = isSystemApp &&
|
||||||
|
launchableSystemApps.contains(packageInfo.packageName),
|
||||||
)
|
)
|
||||||
}.state = packageState
|
}.apply {
|
||||||
|
state = packageState
|
||||||
|
// update name, if none was set, yet (can happen while migrating to storing names)
|
||||||
|
if (this.name == null) {
|
||||||
|
this.name = packageInfo.applicationInfo?.loadLabel(context.packageManager)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,7 +9,7 @@ import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val metadataModule = module {
|
val metadataModule = module {
|
||||||
single { MetadataManager(androidContext(), get(), get(), get(), get(), get()) }
|
single { MetadataManager(androidContext(), get(), get(), get(), get(), get(), get()) }
|
||||||
single<MetadataWriter> { MetadataWriterImpl(get()) }
|
single<MetadataWriter> { MetadataWriterImpl(get()) }
|
||||||
single<MetadataReader> { MetadataReaderImpl(get()) }
|
single<MetadataReader> { MetadataReaderImpl(get()) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader {
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
val pSize = p.optLong(JSON_PACKAGE_SIZE, -1L)
|
val pSize = p.optLong(JSON_PACKAGE_SIZE, -1L)
|
||||||
|
val pName = p.optString(JSON_PACKAGE_APP_NAME)
|
||||||
val pSystem = p.optBoolean(JSON_PACKAGE_SYSTEM, false)
|
val pSystem = p.optBoolean(JSON_PACKAGE_SYSTEM, false)
|
||||||
val pVersion = p.optLong(JSON_PACKAGE_VERSION, 0L)
|
val pVersion = p.optLong(JSON_PACKAGE_VERSION, 0L)
|
||||||
val pInstaller = p.optString(JSON_PACKAGE_INSTALLER)
|
val pInstaller = p.optString(JSON_PACKAGE_INSTALLER)
|
||||||
|
@ -143,7 +144,9 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader {
|
||||||
state = pState,
|
state = pState,
|
||||||
backupType = pBackupType,
|
backupType = pBackupType,
|
||||||
size = if (pSize < 0L) null else pSize,
|
size = if (pSize < 0L) null else pSize,
|
||||||
|
name = if (pName == "") null else pName,
|
||||||
system = pSystem,
|
system = pSystem,
|
||||||
|
isLaunchableSystemApp = p.optBoolean(JSON_PACKAGE_SYSTEM_LAUNCHER, false),
|
||||||
version = if (pVersion == 0L) null else pVersion,
|
version = if (pVersion == 0L) null else pVersion,
|
||||||
installer = if (pInstaller == "") null else pInstaller,
|
installer = if (pInstaller == "") null else pInstaller,
|
||||||
splits = getSplits(p),
|
splits = getSplits(p),
|
||||||
|
|
|
@ -57,8 +57,14 @@ internal class MetadataWriterImpl(private val crypto: Crypto) : MetadataWriter {
|
||||||
if (packageMetadata.size != null) {
|
if (packageMetadata.size != null) {
|
||||||
put(JSON_PACKAGE_SIZE, packageMetadata.size)
|
put(JSON_PACKAGE_SIZE, packageMetadata.size)
|
||||||
}
|
}
|
||||||
|
if (packageMetadata.name != null) {
|
||||||
|
put(JSON_PACKAGE_APP_NAME, packageMetadata.name)
|
||||||
|
}
|
||||||
if (packageMetadata.system) {
|
if (packageMetadata.system) {
|
||||||
put(JSON_PACKAGE_SYSTEM, packageMetadata.system)
|
put(JSON_PACKAGE_SYSTEM, true)
|
||||||
|
}
|
||||||
|
if (packageMetadata.isLaunchableSystemApp) {
|
||||||
|
put(JSON_PACKAGE_SYSTEM_LAUNCHER, true)
|
||||||
}
|
}
|
||||||
packageMetadata.version?.let { put(JSON_PACKAGE_VERSION, it) }
|
packageMetadata.version?.let { put(JSON_PACKAGE_VERSION, it) }
|
||||||
packageMetadata.installer?.let { put(JSON_PACKAGE_INSTALLER, it) }
|
packageMetadata.installer?.let { put(JSON_PACKAGE_INSTALLER, it) }
|
||||||
|
|
|
@ -7,11 +7,7 @@ package com.stevesoltys.seedvault.settings
|
||||||
|
|
||||||
import android.annotation.StringRes
|
import android.annotation.StringRes
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.content.Intent.ACTION_MAIN
|
|
||||||
import android.content.Intent.CATEGORY_LAUNCHER
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.PackageManager.MATCH_SYSTEM_ONLY
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
|
@ -84,10 +80,6 @@ internal class AppListRetriever(
|
||||||
Pair(PACKAGE_NAME_CALL_LOG, R.string.backup_call_log),
|
Pair(PACKAGE_NAME_CALL_LOG, R.string.backup_call_log),
|
||||||
Pair(PACKAGE_NAME_CONTACTS, R.string.backup_contacts)
|
Pair(PACKAGE_NAME_CONTACTS, R.string.backup_contacts)
|
||||||
)
|
)
|
||||||
// filter intent for apps with a launcher activity
|
|
||||||
val i = Intent(ACTION_MAIN).apply {
|
|
||||||
addCategory(CATEGORY_LAUNCHER)
|
|
||||||
}
|
|
||||||
return specialPackages.map { (packageName, stringId) ->
|
return specialPackages.map { (packageName, stringId) ->
|
||||||
val metadata = metadataManager.getPackageMetadata(packageName)
|
val metadata = metadataManager.getPackageMetadata(packageName)
|
||||||
val status = if (packageName == PACKAGE_NAME_CONTACTS && metadata?.state == null) {
|
val status = if (packageName == PACKAGE_NAME_CONTACTS && metadata?.state == null) {
|
||||||
|
@ -105,7 +97,7 @@ internal class AppListRetriever(
|
||||||
status = status,
|
status = status,
|
||||||
isSpecial = true
|
isSpecial = true
|
||||||
)
|
)
|
||||||
} + context.packageManager.queryIntentActivities(i, MATCH_SYSTEM_ONLY).map {
|
} + packageService.launchableSystemApps.map {
|
||||||
val packageName = it.activityInfo.packageName
|
val packageName = it.activityInfo.packageName
|
||||||
val metadata = metadataManager.getPackageMetadata(packageName)
|
val metadata = metadataManager.getPackageMetadata(packageName)
|
||||||
AppStatus(
|
AppStatus(
|
||||||
|
|
|
@ -7,6 +7,9 @@ package com.stevesoltys.seedvault.transport.backup
|
||||||
|
|
||||||
import android.app.backup.IBackupManager
|
import android.app.backup.IBackupManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.Intent.ACTION_MAIN
|
||||||
|
import android.content.Intent.CATEGORY_LAUNCHER
|
||||||
import android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP
|
import android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP
|
||||||
import android.content.pm.ApplicationInfo.FLAG_STOPPED
|
import android.content.pm.ApplicationInfo.FLAG_STOPPED
|
||||||
import android.content.pm.ApplicationInfo.FLAG_SYSTEM
|
import android.content.pm.ApplicationInfo.FLAG_SYSTEM
|
||||||
|
@ -16,6 +19,8 @@ import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.PackageManager.GET_INSTRUMENTATION
|
import android.content.pm.PackageManager.GET_INSTRUMENTATION
|
||||||
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
|
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
|
||||||
|
import android.content.pm.PackageManager.MATCH_SYSTEM_ONLY
|
||||||
|
import android.content.pm.ResolveInfo
|
||||||
import android.os.RemoteException
|
import android.os.RemoteException
|
||||||
import android.os.UserHandle
|
import android.os.UserHandle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
@ -147,6 +152,16 @@ internal class PackageService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val launchableSystemApps: List<ResolveInfo>
|
||||||
|
@WorkerThread
|
||||||
|
get() {
|
||||||
|
// filter intent for apps with a launcher activity
|
||||||
|
val i = Intent(ACTION_MAIN).apply {
|
||||||
|
addCategory(CATEGORY_LAUNCHER)
|
||||||
|
}
|
||||||
|
return packageManager.queryIntentActivities(i, MATCH_SYSTEM_ONLY)
|
||||||
|
}
|
||||||
|
|
||||||
fun getVersionName(packageName: String): String? = try {
|
fun getVersionName(packageName: String): String? = try {
|
||||||
packageManager.getPackageInfo(packageName, 0).versionName
|
packageManager.getPackageInfo(packageName, 0).versionName
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
|
|
@ -16,8 +16,10 @@ import com.stevesoltys.seedvault.metadata.metadataModule
|
||||||
import com.stevesoltys.seedvault.plugins.saf.storagePluginModuleSaf
|
import com.stevesoltys.seedvault.plugins.saf.storagePluginModuleSaf
|
||||||
import com.stevesoltys.seedvault.restore.install.installModule
|
import com.stevesoltys.seedvault.restore.install.installModule
|
||||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
|
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||||
import com.stevesoltys.seedvault.transport.backup.backupModule
|
import com.stevesoltys.seedvault.transport.backup.backupModule
|
||||||
import com.stevesoltys.seedvault.transport.restore.restoreModule
|
import com.stevesoltys.seedvault.transport.restore.restoreModule
|
||||||
|
import io.mockk.mockk
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.KoinApplication
|
import org.koin.core.KoinApplication
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
|
@ -33,9 +35,11 @@ class TestApp : App() {
|
||||||
single<KeyManager> { KeyManagerTestImpl() }
|
single<KeyManager> { KeyManagerTestImpl() }
|
||||||
single<Crypto> { CryptoImpl(get(), get(), get()) }
|
single<Crypto> { CryptoImpl(get(), get(), get()) }
|
||||||
}
|
}
|
||||||
|
private val packageService: PackageService = mockk()
|
||||||
private val appModule = module {
|
private val appModule = module {
|
||||||
single { Clock() }
|
single { Clock() }
|
||||||
single { SettingsManager(this@TestApp) }
|
single { SettingsManager(this@TestApp) }
|
||||||
|
single<PackageService> { packageService }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startKoin(): KoinApplication {
|
override fun startKoin(): KoinApplication {
|
||||||
|
|
|
@ -7,11 +7,13 @@ package com.stevesoltys.seedvault.metadata
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Context.MODE_PRIVATE
|
import android.content.Context.MODE_PRIVATE
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP
|
import android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP
|
||||||
import android.content.pm.ApplicationInfo.FLAG_SYSTEM
|
import android.content.pm.ApplicationInfo.FLAG_SYSTEM
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.pm.ResolveInfo
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.stevesoltys.seedvault.Clock
|
import com.stevesoltys.seedvault.Clock
|
||||||
|
@ -27,6 +29,7 @@ import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
||||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
|
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||||
import io.mockk.Runs
|
import io.mockk.Runs
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
|
@ -64,6 +67,7 @@ class MetadataManagerTest {
|
||||||
private val crypto: Crypto = mockk()
|
private val crypto: Crypto = mockk()
|
||||||
private val metadataWriter: MetadataWriter = mockk()
|
private val metadataWriter: MetadataWriter = mockk()
|
||||||
private val metadataReader: MetadataReader = mockk()
|
private val metadataReader: MetadataReader = mockk()
|
||||||
|
private val packageService: PackageService = mockk()
|
||||||
private val settingsManager: SettingsManager = mockk()
|
private val settingsManager: SettingsManager = mockk()
|
||||||
|
|
||||||
private val manager = MetadataManager(
|
private val manager = MetadataManager(
|
||||||
|
@ -72,9 +76,12 @@ class MetadataManagerTest {
|
||||||
crypto = crypto,
|
crypto = crypto,
|
||||||
metadataWriter = metadataWriter,
|
metadataWriter = metadataWriter,
|
||||||
metadataReader = metadataReader,
|
metadataReader = metadataReader,
|
||||||
settingsManager = settingsManager
|
packageService = packageService,
|
||||||
|
settingsManager = settingsManager,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val packageManager: PackageManager = mockk()
|
||||||
|
|
||||||
private val time = 42L
|
private val time = 42L
|
||||||
private val token = Random.nextLong()
|
private val token = Random.nextLong()
|
||||||
private val packageName = getRandomString()
|
private val packageName = getRandomString()
|
||||||
|
@ -162,6 +169,7 @@ class MetadataManagerTest {
|
||||||
signatures = listOf("sig")
|
signatures = listOf("sig")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectModifyMetadata(initialMetadata)
|
expectModifyMetadata(initialMetadata)
|
||||||
|
|
||||||
|
@ -185,12 +193,23 @@ class MetadataManagerTest {
|
||||||
signatures = listOf("sig")
|
signatures = listOf("sig")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
|
every { packageService.launchableSystemApps } returns listOf(
|
||||||
|
ResolveInfo().apply {
|
||||||
|
activityInfo = ActivityInfo().apply {
|
||||||
|
packageName = this@MetadataManagerTest.packageName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectModifyMetadata(initialMetadata)
|
expectModifyMetadata(initialMetadata)
|
||||||
|
|
||||||
manager.onApkBackedUp(packageInfo, packageMetadata)
|
manager.onApkBackedUp(packageInfo, packageMetadata)
|
||||||
|
|
||||||
assertEquals(packageMetadata.copy(system = true), manager.getPackageMetadata(packageName))
|
assertEquals(
|
||||||
|
packageMetadata.copy(system = true, isLaunchableSystemApp = true),
|
||||||
|
manager.getPackageMetadata(packageName),
|
||||||
|
)
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
cacheInputStream.close()
|
cacheInputStream.close()
|
||||||
|
@ -214,6 +233,7 @@ class MetadataManagerTest {
|
||||||
signatures = listOf("sig foo")
|
signatures = listOf("sig foo")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectWriteToCache(initialMetadata)
|
expectWriteToCache(initialMetadata)
|
||||||
|
|
||||||
|
@ -236,6 +256,7 @@ class MetadataManagerTest {
|
||||||
signatures = listOf("sig")
|
signatures = listOf("sig")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectWriteToCache(initialMetadata)
|
expectWriteToCache(initialMetadata)
|
||||||
val oldState = UNKNOWN_ERROR
|
val oldState = UNKNOWN_ERROR
|
||||||
|
@ -295,6 +316,7 @@ class MetadataManagerTest {
|
||||||
signatures = listOf("sig")
|
signatures = listOf("sig")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
|
|
||||||
assertNull(manager.getPackageMetadata(packageName))
|
assertNull(manager.getPackageMetadata(packageName))
|
||||||
|
@ -330,6 +352,8 @@ class MetadataManagerTest {
|
||||||
val packageMetadata = PackageMetadata(time)
|
val packageMetadata = PackageMetadata(time)
|
||||||
updatedMetadata.packageMetadataMap[packageName] = packageMetadata
|
updatedMetadata.packageMetadataMap[packageName] = packageMetadata
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
|
every { packageService.launchableSystemApps } returns emptyList()
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
every { clock.time() } returns time
|
every { clock.time() } returns time
|
||||||
expectModifyMetadata(initialMetadata)
|
expectModifyMetadata(initialMetadata)
|
||||||
|
@ -342,6 +366,7 @@ class MetadataManagerTest {
|
||||||
backupType = BackupType.FULL,
|
backupType = BackupType.FULL,
|
||||||
size = size,
|
size = size,
|
||||||
system = true,
|
system = true,
|
||||||
|
isLaunchableSystemApp = false,
|
||||||
),
|
),
|
||||||
manager.getPackageMetadata(packageName)
|
manager.getPackageMetadata(packageName)
|
||||||
)
|
)
|
||||||
|
@ -361,6 +386,7 @@ class MetadataManagerTest {
|
||||||
expectModifyMetadata(initialMetadata)
|
expectModifyMetadata(initialMetadata)
|
||||||
|
|
||||||
every { settingsManager.d2dBackupsEnabled() } returns true
|
every { settingsManager.d2dBackupsEnabled() } returns true
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
|
|
||||||
manager.onPackageBackedUp(packageInfo, BackupType.FULL, 0L, storageOutputStream)
|
manager.onPackageBackedUp(packageInfo, BackupType.FULL, 0L, storageOutputStream)
|
||||||
assertTrue(initialMetadata.d2dBackup)
|
assertTrue(initialMetadata.d2dBackup)
|
||||||
|
@ -382,6 +408,7 @@ class MetadataManagerTest {
|
||||||
updatedMetadata.packageMetadataMap[packageName] =
|
updatedMetadata.packageMetadataMap[packageName] =
|
||||||
PackageMetadata(updateTime, APK_AND_DATA, BackupType.KV, size)
|
PackageMetadata(updateTime, APK_AND_DATA, BackupType.KV, size)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
every { clock.time() } returns updateTime
|
every { clock.time() } returns updateTime
|
||||||
every { metadataWriter.write(updatedMetadata, storageOutputStream) } throws IOException()
|
every { metadataWriter.write(updatedMetadata, storageOutputStream) } throws IOException()
|
||||||
|
@ -414,6 +441,7 @@ class MetadataManagerTest {
|
||||||
PackageMetadata(time, state = APK_AND_DATA)
|
PackageMetadata(time, state = APK_AND_DATA)
|
||||||
|
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
every { clock.time() } returns time
|
every { clock.time() } returns time
|
||||||
expectModifyMetadata(updatedMetadata)
|
expectModifyMetadata(updatedMetadata)
|
||||||
|
|
||||||
|
@ -437,6 +465,7 @@ class MetadataManagerTest {
|
||||||
val updatedMetadata = initialMetadata.copy()
|
val updatedMetadata = initialMetadata.copy()
|
||||||
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = NOT_ALLOWED)
|
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = NOT_ALLOWED)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectWriteToCache(updatedMetadata)
|
expectWriteToCache(updatedMetadata)
|
||||||
|
|
||||||
|
@ -454,6 +483,7 @@ class MetadataManagerTest {
|
||||||
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = WAS_STOPPED)
|
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = WAS_STOPPED)
|
||||||
initialMetadata.packageMetadataMap.remove(packageName)
|
initialMetadata.packageMetadataMap.remove(packageName)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectWriteToCache(updatedMetadata)
|
expectWriteToCache(updatedMetadata)
|
||||||
|
|
||||||
|
@ -482,6 +512,7 @@ class MetadataManagerTest {
|
||||||
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = WAS_STOPPED)
|
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = WAS_STOPPED)
|
||||||
initialMetadata.packageMetadataMap.remove(packageName)
|
initialMetadata.packageMetadataMap.remove(packageName)
|
||||||
|
|
||||||
|
every { context.packageManager } returns packageManager
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
expectModifyMetadata(updatedMetadata)
|
expectModifyMetadata(updatedMetadata)
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,10 @@ internal class MetadataWriterDecoderTest {
|
||||||
time = Random.nextLong(),
|
time = Random.nextLong(),
|
||||||
state = APK_AND_DATA,
|
state = APK_AND_DATA,
|
||||||
backupType = BackupType.FULL,
|
backupType = BackupType.FULL,
|
||||||
|
size = Random.nextLong(0, Long.MAX_VALUE),
|
||||||
|
name = getRandomString(),
|
||||||
|
system = Random.nextBoolean(),
|
||||||
|
isLaunchableSystemApp = Random.nextBoolean(),
|
||||||
version = Random.nextLong(),
|
version = Random.nextLong(),
|
||||||
installer = getRandomString(),
|
installer = getRandomString(),
|
||||||
splits = listOf(
|
splits = listOf(
|
||||||
|
@ -94,6 +98,7 @@ internal class MetadataWriterDecoderTest {
|
||||||
time = Random.nextLong(),
|
time = Random.nextLong(),
|
||||||
state = QUOTA_EXCEEDED,
|
state = QUOTA_EXCEEDED,
|
||||||
backupType = BackupType.FULL,
|
backupType = BackupType.FULL,
|
||||||
|
name = null,
|
||||||
size = Random.nextLong(0..Long.MAX_VALUE),
|
size = Random.nextLong(0..Long.MAX_VALUE),
|
||||||
system = Random.nextBoolean(),
|
system = Random.nextBoolean(),
|
||||||
version = Random.nextLong(),
|
version = Random.nextLong(),
|
||||||
|
@ -108,6 +113,7 @@ internal class MetadataWriterDecoderTest {
|
||||||
state = NO_DATA,
|
state = NO_DATA,
|
||||||
backupType = BackupType.KV,
|
backupType = BackupType.KV,
|
||||||
size = null,
|
size = null,
|
||||||
|
name = getRandomString(),
|
||||||
system = Random.nextBoolean(),
|
system = Random.nextBoolean(),
|
||||||
version = Random.nextLong(),
|
version = Random.nextLong(),
|
||||||
installer = getRandomString(),
|
installer = getRandomString(),
|
||||||
|
@ -121,6 +127,7 @@ internal class MetadataWriterDecoderTest {
|
||||||
state = NOT_ALLOWED,
|
state = NOT_ALLOWED,
|
||||||
size = 0,
|
size = 0,
|
||||||
system = Random.nextBoolean(),
|
system = Random.nextBoolean(),
|
||||||
|
isLaunchableSystemApp = Random.nextBoolean(),
|
||||||
version = Random.nextLong(),
|
version = Random.nextLong(),
|
||||||
installer = getRandomString(),
|
installer = getRandomString(),
|
||||||
sha256 = getRandomString(),
|
sha256 = getRandomString(),
|
||||||
|
@ -138,10 +145,11 @@ internal class MetadataWriterDecoderTest {
|
||||||
private fun getMetadata(
|
private fun getMetadata(
|
||||||
packageMetadata: HashMap<String, PackageMetadata> = HashMap(),
|
packageMetadata: HashMap<String, PackageMetadata> = HashMap(),
|
||||||
): BackupMetadata {
|
): BackupMetadata {
|
||||||
|
val version = Random.nextBytes(1)[0]
|
||||||
return BackupMetadata(
|
return BackupMetadata(
|
||||||
version = Random.nextBytes(1)[0],
|
version = version,
|
||||||
token = Random.nextLong(),
|
token = Random.nextLong(),
|
||||||
salt = getRandomBase64(32),
|
salt = if (version != 0.toByte()) getRandomBase64(32) else "",
|
||||||
time = Random.nextLong(),
|
time = Random.nextLong(),
|
||||||
androidVersion = Random.nextInt(),
|
androidVersion = Random.nextInt(),
|
||||||
androidIncremental = getRandomString(),
|
androidIncremental = getRandomString(),
|
||||||
|
|
Loading…
Reference in a new issue