Add support for d2d
Co-authored-by: Tommy Webb <tommy@calyxinstitute.org> Change-Id: I61d88a511a0f81e6d384e3650f6797725da79814
This commit is contained in:
parent
befc1b1a77
commit
f0575ddd6a
10 changed files with 123 additions and 50 deletions
|
|
@ -5,7 +5,10 @@ import android.hardware.usb.UsbDevice
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
import androidx.annotation.UiThread
|
import androidx.annotation.UiThread
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.annotation.VisibleForTesting.PRIVATE
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
|
@ -34,6 +37,8 @@ private const val PREF_KEY_UNLIMITED_QUOTA = "unlimited_quota"
|
||||||
|
|
||||||
class SettingsManager(private val context: Context) {
|
class SettingsManager(private val context: Context) {
|
||||||
|
|
||||||
|
private val TAG = SettingsManager::class.java.simpleName
|
||||||
|
|
||||||
private val prefs = permitDiskReads {
|
private val prefs = permitDiskReads {
|
||||||
PreferenceManager.getDefaultSharedPreferences(context)
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
}
|
}
|
||||||
|
|
@ -41,12 +46,19 @@ class SettingsManager(private val context: Context) {
|
||||||
@Volatile
|
@Volatile
|
||||||
private var token: Long? = null
|
private var token: Long? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The StoragePlugin provider package name is assigned by ConfigurableBackupTransportService
|
||||||
|
* to ensure it is excluded; if the provider is killed, the backup or restore will fail.
|
||||||
|
*/
|
||||||
|
var pluginProviderPackageName: String? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This gets accessed by non-UI threads when saving with [PreferenceManager]
|
* This gets accessed by non-UI threads when saving with [PreferenceManager]
|
||||||
* and when [isBackupEnabled] is called during a backup run.
|
* and when [isBackupEnabled] is called during a backup run.
|
||||||
* Therefore, it is implemented with a thread-safe [ConcurrentSkipListSet].
|
* Therefore, it is implemented with a thread-safe [ConcurrentSkipListSet].
|
||||||
*/
|
*/
|
||||||
private val blacklistedApps: MutableSet<String> by lazy {
|
@VisibleForTesting(otherwise = PRIVATE)
|
||||||
|
val blacklistedApps: MutableSet<String> by lazy {
|
||||||
ConcurrentSkipListSet(prefs.getStringSet(PREF_KEY_BACKUP_APP_BLACKLIST, emptySet()))
|
ConcurrentSkipListSet(prefs.getStringSet(PREF_KEY_BACKUP_APP_BLACKLIST, emptySet()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,6 +144,15 @@ class SettingsManager(private val context: Context) {
|
||||||
|
|
||||||
fun isBackupEnabled(packageName: String) = !blacklistedApps.contains(packageName)
|
fun isBackupEnabled(packageName: String) = !blacklistedApps.contains(packageName)
|
||||||
|
|
||||||
|
fun isAppAllowedForBackup(packageName: String): Boolean {
|
||||||
|
// Check that the app is not blacklisted by the user
|
||||||
|
val enabled = isBackupEnabled(packageName)
|
||||||
|
if (!enabled) Log.w(TAG, "Backup of $packageName disabled by user.")
|
||||||
|
// We need to exclude the DocumentsProvider used to store backup data.
|
||||||
|
// Otherwise, it gets killed when we back it up, terminating our backup.
|
||||||
|
return enabled && packageName != pluginProviderPackageName
|
||||||
|
}
|
||||||
|
|
||||||
fun isStorageBackupEnabled() = prefs.getBoolean(PREF_KEY_BACKUP_STORAGE, false)
|
fun isStorageBackupEnabled() = prefs.getBoolean(PREF_KEY_BACKUP_STORAGE, false)
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import android.os.ParcelFileDescriptor
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.stevesoltys.seedvault.R
|
import com.stevesoltys.seedvault.R
|
||||||
import com.stevesoltys.seedvault.settings.SettingsActivity
|
import com.stevesoltys.seedvault.settings.SettingsActivity
|
||||||
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
|
import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
|
||||||
import com.stevesoltys.seedvault.transport.restore.RestoreCoordinator
|
import com.stevesoltys.seedvault.transport.restore.RestoreCoordinator
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
@ -22,7 +23,7 @@ import org.koin.core.component.inject
|
||||||
val TRANSPORT_ID: String = ConfigurableBackupTransport::class.java.name
|
val TRANSPORT_ID: String = ConfigurableBackupTransport::class.java.name
|
||||||
|
|
||||||
// Since there seems to be consensus in the community to pose as device to device transport,
|
// Since there seems to be consensus in the community to pose as device to device transport,
|
||||||
// we are pretending to be one here. This will backup opt-out apps that target API 30.
|
// we are pretending to be one here. This will backup opt-out apps that target at least API 30.
|
||||||
const val TRANSPORT_FLAGS = FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED or FLAG_DEVICE_TO_DEVICE_TRANSFER
|
const val TRANSPORT_FLAGS = FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED or FLAG_DEVICE_TO_DEVICE_TRANSFER
|
||||||
|
|
||||||
private const val TRANSPORT_DIRECTORY_NAME =
|
private const val TRANSPORT_DIRECTORY_NAME =
|
||||||
|
|
@ -38,6 +39,7 @@ class ConfigurableBackupTransport internal constructor(private val context: Cont
|
||||||
|
|
||||||
private val backupCoordinator by inject<BackupCoordinator>()
|
private val backupCoordinator by inject<BackupCoordinator>()
|
||||||
private val restoreCoordinator by inject<RestoreCoordinator>()
|
private val restoreCoordinator by inject<RestoreCoordinator>()
|
||||||
|
private val settingsManager by inject<SettingsManager>()
|
||||||
|
|
||||||
override fun transportDirName(): String {
|
override fun transportDirName(): String {
|
||||||
return TRANSPORT_DIRECTORY_NAME
|
return TRANSPORT_DIRECTORY_NAME
|
||||||
|
|
@ -120,9 +122,9 @@ class ConfigurableBackupTransport internal constructor(private val context: Cont
|
||||||
|
|
||||||
override fun isAppEligibleForBackup(
|
override fun isAppEligibleForBackup(
|
||||||
targetPackage: PackageInfo,
|
targetPackage: PackageInfo,
|
||||||
isFullBackup: Boolean,
|
@Suppress("UNUSED_PARAMETER") isFullBackup: Boolean,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return backupCoordinator.isAppEligibleForBackup(targetPackage, isFullBackup)
|
return settingsManager.isAppAllowedForBackup(targetPackage.packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long = runBlocking {
|
override fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long = runBlocking {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import android.util.Log
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import com.stevesoltys.seedvault.BackupMonitor
|
import com.stevesoltys.seedvault.BackupMonitor
|
||||||
import com.stevesoltys.seedvault.crypto.KeyManager
|
import com.stevesoltys.seedvault.crypto.KeyManager
|
||||||
|
import com.stevesoltys.seedvault.plugins.StoragePlugin
|
||||||
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
import com.stevesoltys.seedvault.transport.backup.PackageService
|
import com.stevesoltys.seedvault.transport.backup.PackageService
|
||||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||||
import com.stevesoltys.seedvault.ui.notification.NotificationBackupObserver
|
import com.stevesoltys.seedvault.ui.notification.NotificationBackupObserver
|
||||||
|
|
@ -65,7 +67,13 @@ fun requestBackup(context: Context) {
|
||||||
val backupManager: IBackupManager = get().get()
|
val backupManager: IBackupManager = get().get()
|
||||||
if (backupManager.isBackupEnabled) {
|
if (backupManager.isBackupEnabled) {
|
||||||
val packageService: PackageService = get().get()
|
val packageService: PackageService = get().get()
|
||||||
val packages = packageService.eligiblePackages
|
val settingsManager: SettingsManager = get().get()
|
||||||
|
val plugin: StoragePlugin = get().get()
|
||||||
|
|
||||||
|
// SettingsManager must know the StoragePlugin provider package name to exclude it when
|
||||||
|
// isAppAllowedForBackup is called.
|
||||||
|
settingsManager.pluginProviderPackageName = plugin.providerPackageName
|
||||||
|
val packages = packageService.requestedPackages
|
||||||
val appTotals = packageService.expectedAppTotals
|
val appTotals = packageService.expectedAppTotals
|
||||||
|
|
||||||
val result = try {
|
val result = try {
|
||||||
|
|
|
||||||
|
|
@ -138,19 +138,6 @@ internal class BackupCoordinator(
|
||||||
TRANSPORT_ERROR
|
TRANSPORT_ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isAppEligibleForBackup(
|
|
||||||
targetPackage: PackageInfo,
|
|
||||||
@Suppress("UNUSED_PARAMETER") isFullBackup: Boolean,
|
|
||||||
): Boolean {
|
|
||||||
val packageName = targetPackage.packageName
|
|
||||||
// Check that the app is not blacklisted by the user
|
|
||||||
val enabled = settingsManager.isBackupEnabled(packageName)
|
|
||||||
if (!enabled) Log.w(TAG, "Backup of $packageName disabled by user.")
|
|
||||||
// We need to exclude the DocumentsProvider used to store backup data.
|
|
||||||
// Otherwise, it gets killed when we back it up, terminating our backup.
|
|
||||||
return enabled && targetPackage.packageName != plugin.providerPackageName
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ask the transport about current quota for backup size of the package.
|
* Ask the transport about current quota for backup size of the package.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ val backupModule = module {
|
||||||
single {
|
single {
|
||||||
PackageService(
|
PackageService(
|
||||||
context = androidContext(),
|
context = androidContext(),
|
||||||
backupManager = get()
|
settingsManager = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
single {
|
single {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.stevesoltys.seedvault.transport.backup
|
package com.stevesoltys.seedvault.transport.backup
|
||||||
|
|
||||||
import android.app.backup.IBackupManager
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
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
|
||||||
|
|
@ -17,6 +16,7 @@ import android.util.Log
|
||||||
import android.util.Log.INFO
|
import android.util.Log.INFO
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
|
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
|
||||||
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
|
|
||||||
private val TAG = PackageService::class.java.simpleName
|
private val TAG = PackageService::class.java.simpleName
|
||||||
|
|
||||||
|
|
@ -28,13 +28,13 @@ private const val LOG_MAX_PACKAGES = 100
|
||||||
*/
|
*/
|
||||||
internal class PackageService(
|
internal class PackageService(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val backupManager: IBackupManager,
|
private val settingsManager: SettingsManager,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val packageManager: PackageManager = context.packageManager
|
private val packageManager: PackageManager = context.packageManager
|
||||||
private val myUserId = UserHandle.myUserId()
|
private val myUserId = UserHandle.myUserId()
|
||||||
|
|
||||||
val eligiblePackages: Array<String>
|
val requestedPackages: Array<String>
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
@Throws(RemoteException::class)
|
@Throws(RemoteException::class)
|
||||||
get() {
|
get() {
|
||||||
|
|
@ -45,22 +45,27 @@ internal class PackageService(
|
||||||
// log packages
|
// log packages
|
||||||
if (Log.isLoggable(TAG, INFO)) {
|
if (Log.isLoggable(TAG, INFO)) {
|
||||||
Log.i(TAG, "Got ${packages.size} packages:")
|
Log.i(TAG, "Got ${packages.size} packages:")
|
||||||
packages.chunked(LOG_MAX_PACKAGES).forEach {
|
logPackages(packages)
|
||||||
Log.i(TAG, it.toString())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val eligibleApps =
|
// As a result of switching to D2D, we can no longer use BackupManager's method
|
||||||
backupManager.filterAppsEligibleForBackupForUser(myUserId, packages.toTypedArray())
|
// filterAppsEligibleForBackupForUser, because it will not return all of the expected
|
||||||
|
// apps; it is not designed to determine MIGRATION eligibility, only eligibility for
|
||||||
|
// inclusion in *scheduled* BACKUPs, which are implicitly *not* D2D migrations.
|
||||||
|
// None of the other eligibility methods are exposed by AOSP APIs. On the other hand,
|
||||||
|
// the actual backup process properly utilizes OperationType.MIGRATION and performs its
|
||||||
|
// own checks as to whether apps are allowed to be backed up. All we need to do now is
|
||||||
|
// filter out apps that *we* want to be excluded. The system will do the rest later.
|
||||||
|
val requestedApps = packages.filter { settingsManager.isAppAllowedForBackup(it) }
|
||||||
|
|
||||||
// log eligible packages
|
// log requested packages
|
||||||
if (Log.isLoggable(TAG, INFO)) {
|
if (Log.isLoggable(TAG, INFO)) {
|
||||||
Log.i(TAG, "Filtering left ${eligibleApps.size} eligible packages:")
|
Log.i(TAG, "Filtering left ${requestedApps.size} requested packages:")
|
||||||
logPackages(eligibleApps.toList())
|
logPackages(requestedApps.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
// add magic @pm@ package (PACKAGE_MANAGER_SENTINEL) which holds package manager data
|
// add magic @pm@ package (PACKAGE_MANAGER_SENTINEL) which holds package manager data
|
||||||
val packageArray = eligibleApps.toMutableList()
|
val packageArray = requestedApps.toMutableList()
|
||||||
packageArray.add(MAGIC_PACKAGE_MANAGER)
|
packageArray.add(MAGIC_PACKAGE_MANAGER)
|
||||||
|
|
||||||
return packageArray.toTypedArray()
|
return packageArray.toTypedArray()
|
||||||
|
|
@ -159,7 +164,13 @@ internal fun PackageInfo.isSystemApp(): Boolean {
|
||||||
|
|
||||||
internal fun PackageInfo.allowsBackup(): Boolean {
|
internal fun PackageInfo.allowsBackup(): Boolean {
|
||||||
if (packageName == MAGIC_PACKAGE_MANAGER || applicationInfo == null) return false
|
if (packageName == MAGIC_PACKAGE_MANAGER || applicationInfo == null) return false
|
||||||
return applicationInfo.flags and FLAG_ALLOW_BACKUP != 0
|
|
||||||
|
// At backup time, the system will filter out any apps that *it* does not want to be backed up.
|
||||||
|
// Now that we have switched to D2D, *we* generally want to back up as much as possible;
|
||||||
|
// part of the point of D2D is to ignore FLAG_ALLOW_BACKUP (allowsBackup). So, we return true.
|
||||||
|
// See frameworks/base/services/backup/java/com/android/server/backup/utils/
|
||||||
|
// BackupEligibilityRules.java lines 74-81 and 163-167 (tag: android-13.0.0_r8).
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,14 @@ import com.stevesoltys.seedvault.transport.TRANSPORT_FLAGS
|
||||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device name used in AOSP to indicate that a restore set is part of a device-to-device migration.
|
||||||
|
* See getBackupEligibilityRules in frameworks/base/services/backup/java/com/android/server/
|
||||||
|
* backup/restore/ActiveRestoreSession.java. AOSP currently relies on this constant, and it is not
|
||||||
|
* publicly exposed. Framework code indicates they intend to use a flag, instead, in the future.
|
||||||
|
*/
|
||||||
|
internal const val DEVICE_NAME_FOR_D2D_SET = "D2D"
|
||||||
|
|
||||||
private data class RestoreCoordinatorState(
|
private data class RestoreCoordinatorState(
|
||||||
val token: Long,
|
val token: Long,
|
||||||
val packages: Iterator<PackageInfo>,
|
val packages: Iterator<PackageInfo>,
|
||||||
|
|
@ -92,7 +100,8 @@ internal class RestoreCoordinator(
|
||||||
**/
|
**/
|
||||||
suspend fun getAvailableRestoreSets(): Array<RestoreSet>? {
|
suspend fun getAvailableRestoreSets(): Array<RestoreSet>? {
|
||||||
return getAvailableMetadata()?.map { (_, metadata) ->
|
return getAvailableMetadata()?.map { (_, metadata) ->
|
||||||
RestoreSet(metadata.deviceName, metadata.deviceName, metadata.token, TRANSPORT_FLAGS)
|
RestoreSet(metadata.deviceName /* name */, DEVICE_NAME_FOR_D2D_SET /* device */,
|
||||||
|
metadata.token, TRANSPORT_FLAGS)
|
||||||
}?.toTypedArray()
|
}?.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.stevesoltys.seedvault.settings
|
||||||
|
|
||||||
|
import com.stevesoltys.seedvault.transport.TransportTest
|
||||||
|
import io.mockk.every
|
||||||
|
import org.junit.jupiter.api.Assertions.assertFalse
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
internal class SettingsManagerTest : TransportTest() {
|
||||||
|
|
||||||
|
private val userExcludedApps = mutableSetOf<String>()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `isAppAllowedForBackup() exempts plugin provider and blacklisted apps`() {
|
||||||
|
val pkgName = packageInfo.packageName
|
||||||
|
|
||||||
|
preparePartiallyMockedSettingsManager()
|
||||||
|
|
||||||
|
settingsManager.pluginProviderPackageName = pkgName
|
||||||
|
assertFalse(settingsManager.isAppAllowedForBackup(pkgName),
|
||||||
|
"Plugin provider must not be allowed for backup")
|
||||||
|
|
||||||
|
settingsManager.pluginProviderPackageName = "new.package"
|
||||||
|
userExcludedApps.add(pkgName)
|
||||||
|
assertFalse(settingsManager.isAppAllowedForBackup(pkgName),
|
||||||
|
"Excluded package must not be allowed for backup")
|
||||||
|
|
||||||
|
userExcludedApps.remove(pkgName)
|
||||||
|
assertTrue(settingsManager.isAppAllowedForBackup(pkgName),
|
||||||
|
"Non-excluded package should be allowed if it is not the plugin provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun preparePartiallyMockedSettingsManager() {
|
||||||
|
// Use our own app exclusion set to avoid the uninitialized set in mocked SettingsManager.
|
||||||
|
every { settingsManager.blacklistedApps } answers { userExcludedApps }
|
||||||
|
|
||||||
|
// Always call isAppAllowedForBackup unmocked rather than an unimplemented mock.
|
||||||
|
every { settingsManager.isAppAllowedForBackup(any()) } answers { callOriginal() }
|
||||||
|
|
||||||
|
// Always call isBackupEnabled unmocked.
|
||||||
|
every { settingsManager.isBackupEnabled(any()) } answers { callOriginal() }
|
||||||
|
|
||||||
|
// Always set the underlying provider name field for use by unmocked isAppAllowedForBackup.
|
||||||
|
every {
|
||||||
|
settingsManager.pluginProviderPackageName = any()
|
||||||
|
} propertyType String::class answers {
|
||||||
|
fieldValue = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -32,8 +32,6 @@ import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertFalse
|
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
@ -183,20 +181,6 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
verify { metadataOutputStream.close() }
|
verify { metadataOutputStream.close() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `isAppEligibleForBackup() exempts plugin provider and blacklisted apps`() {
|
|
||||||
every {
|
|
||||||
settingsManager.isBackupEnabled(packageInfo.packageName)
|
|
||||||
} returns true andThen false andThen true
|
|
||||||
every {
|
|
||||||
plugin.providerPackageName
|
|
||||||
} returns packageInfo.packageName andThen "new.package" andThen "new.package"
|
|
||||||
|
|
||||||
assertFalse(backup.isAppEligibleForBackup(packageInfo, true))
|
|
||||||
assertFalse(backup.isAppEligibleForBackup(packageInfo, true))
|
|
||||||
assertTrue(backup.isAppEligibleForBackup(packageInfo, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `clearing KV backup data throws`() = runBlocking {
|
fun `clearing KV backup data throws`() = runBlocking {
|
||||||
every { settingsManager.getToken() } returns token
|
every { settingsManager.getToken() } returns token
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
||||||
|
|
||||||
val sets = restore.getAvailableRestoreSets() ?: fail()
|
val sets = restore.getAvailableRestoreSets() ?: fail()
|
||||||
assertEquals(2, sets.size)
|
assertEquals(2, sets.size)
|
||||||
assertEquals(metadata.deviceName, sets[0].device)
|
assertEquals(DEVICE_NAME_FOR_D2D_SET, sets[0].device)
|
||||||
assertEquals(metadata.deviceName, sets[0].name)
|
assertEquals(metadata.deviceName, sets[0].name)
|
||||||
assertEquals(metadata.token, sets[0].token)
|
assertEquals(metadata.token, sets[0].token)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue