diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkRestore.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkRestore.kt index 4c7a9a1a..59600b3d 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkRestore.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/ApkRestore.kt @@ -41,7 +41,12 @@ internal class ApkRestore( @Suppress("BlockingMethodInNonBlockingContext") fun restore(backup: RestorableBackup) = flow { // filter out packages without APK and get total - val packages = backup.packageMetadataMap.filter { it.value.hasApk() } + val packages = backup.packageMetadataMap.filter { + // We also need to exclude the DocumentsProvider used to retrieve backup data. + // Otherwise, it gets killed when we install it, terminating our restoration. + val isStorageProvider = it.key == storagePlugin.providerPackageName + it.value.hasApk() && !isStorageProvider + } val total = packages.size var progress = 0 diff --git a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt index d384e6ac..97012e3d 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt @@ -118,6 +118,7 @@ internal class ApkBackupRestoreTest : TransportTest() { every { metadataManager.salt } returns salt every { crypto.getNameForApk(salt, packageName) } returns name every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkBackup.backupApkIfNecessary(packageInfo, PackageState.APK_AND_DATA, outputStreamGetter) diff --git a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt index 86be9580..ad730402 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt @@ -97,6 +97,7 @@ internal class ApkRestoreTest : TransportTest() { every { strictContext.cacheDir } returns File(tmpDir.toString()) every { crypto.getNameForApk(salt, packageName, "") } returns name coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkRestore.restore(backup).collectIndexed { i, value -> assertQueuedFailFinished(i, value) @@ -112,6 +113,7 @@ internal class ApkRestoreTest : TransportTest() { every { crypto.getNameForApk(salt, packageName, "") } returns name coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkRestore.restore(backup).collectIndexed { i, value -> assertQueuedFailFinished(i, value) @@ -124,6 +126,7 @@ internal class ApkRestoreTest : TransportTest() { coEvery { apkInstaller.install(match { it.size == 1 }, packageName, installerName, any()) } throws SecurityException() + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkRestore.restore(backup).collectIndexed { i, value -> assertQueuedProgressFailFinished(i, value) @@ -146,6 +149,7 @@ internal class ApkRestoreTest : TransportTest() { coEvery { apkInstaller.install(match { it.size == 1 }, packageName, installerName, any()) } returns installResult + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkRestore.restore(backup).collectIndexed { i, value -> assertQueuedProgressSuccessFinished(i, value) @@ -184,6 +188,7 @@ internal class ApkRestoreTest : TransportTest() { coEvery { apkInstaller.install(match { it.size == 1 }, packageName, installerName, any()) } returns installResult + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkRestore.restore(backup).collectIndexed { i, value -> assertQueuedProgressSuccessFinished(i, value) @@ -202,6 +207,7 @@ internal class ApkRestoreTest : TransportTest() { cacheBaseApkAndGetInfo(tmpDir) every { packageInfo.applicationInfo.loadIcon(pm) } returns icon + every { storagePlugin.providerPackageName } returns storageProviderPackageName if (willFail) { every { @@ -281,6 +287,7 @@ internal class ApkRestoreTest : TransportTest() { every { splitCompatChecker.isCompatible(deviceName, listOf(split1Name, split2Name)) } returns false + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkRestore.restore(backup).collectIndexed { i, value -> assertQueuedProgressFailFinished(i, value) @@ -303,6 +310,7 @@ internal class ApkRestoreTest : TransportTest() { coEvery { storagePlugin.getInputStream(token, suffixName) } returns ByteArrayInputStream(getRandomByteArray()) + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkRestore.restore(backup).collectIndexed { i, value -> assertQueuedProgressFailFinished(i, value) @@ -325,6 +333,7 @@ internal class ApkRestoreTest : TransportTest() { every { splitCompatChecker.isCompatible(deviceName, listOf(splitName)) } returns true every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName coEvery { storagePlugin.getInputStream(token, suffixName) } throws IOException() + every { storagePlugin.providerPackageName } returns storageProviderPackageName apkRestore.restore(backup).collectIndexed { i, value -> assertQueuedProgressFailFinished(i, value) @@ -363,6 +372,7 @@ internal class ApkRestoreTest : TransportTest() { coEvery { storagePlugin.getInputStream(token, suffixName1) } returns split1InputStream every { crypto.getNameForApk(salt, packageName, split2Name) } returns suffixName2 coEvery { storagePlugin.getInputStream(token, suffixName2) } returns split2InputStream + every { storagePlugin.providerPackageName } returns storageProviderPackageName coEvery { apkInstaller.install(match { it.size == 3 }, packageName, installerName, any()) @@ -381,6 +391,27 @@ internal class ApkRestoreTest : TransportTest() { } } + @Test + fun `storage provider app does not get reinstalled`(@TempDir tmpDir: Path) = runBlocking { + // set the storage provider package name to match our current package name, + // and ensure that the current package is therefore skipped. + every { storagePlugin.providerPackageName } returns packageName + + apkRestore.restore(backup).collectIndexed { i, value -> + when (i) { + 0 -> { + assertFalse(value.isFinished) + } + 1 -> { + // the only package provided should have been filtered, leaving 0 packages. + assertEquals(0, value.total) + assertTrue(value.isFinished) + } + else -> fail("more values emitted") + } + } + } + private fun swapPackages(packageMetadataMap: PackageMetadataMap): RestorableBackup { val metadata = metadata.copy(packageMetadataMap = packageMetadataMap) return backup.copy(backupMetadata = metadata) diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt index 9d441528..2f4e0cd2 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt @@ -61,6 +61,7 @@ internal abstract class TransportTest { protected val salt = metadata.salt protected val name = getRandomString(12) protected val name2 = getRandomString(23) + protected val storageProviderPackageName = getRandomString(23) init { mockkStatic(Log::class)