Add support for d2d

Change-Id: I61d88a511a0f81e6d384e3650f6797725da79814
This commit is contained in:
Oliver Scott 2022-05-17 15:49:33 +02:00 committed by Chirayu Desai
parent 72c8460864
commit 4a98667162
7 changed files with 73 additions and 25 deletions

View file

@ -67,6 +67,12 @@
android:name="android.permission.INTERACT_ACROSS_USERS_FULL" android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
tools:ignore="ProtectedPermissions" /> tools:ignore="ProtectedPermissions" />
<!-- This is needed to read if the IGNORE_ALLOW_BACKUP_IN_D2D -->
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
<!-- This is needed to read if the -->
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<application <application
android:name=".App" android:name=".App"
android:allowBackup="false" android:allowBackup="false"

View file

@ -5,7 +5,6 @@ import android.app.Application
import android.app.backup.BackupManager.PACKAGE_MANAGER_SENTINEL import android.app.backup.BackupManager.PACKAGE_MANAGER_SENTINEL
import android.app.backup.IBackupManager import android.app.backup.IBackupManager
import android.content.Context import android.content.Context
import android.content.Context.BACKUP_SERVICE
import android.content.pm.PackageManager.PERMISSION_GRANTED import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Build import android.os.Build
import android.os.ServiceManager.getService import android.os.ServiceManager.getService
@ -118,6 +117,8 @@ open class App : Application() {
} }
const val PLATFORM_PACKAGE_NAME = "android"
const val SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup"
const val MAGIC_PACKAGE_MANAGER = PACKAGE_MANAGER_SENTINEL const val MAGIC_PACKAGE_MANAGER = PACKAGE_MANAGER_SENTINEL
const val ANCESTRAL_RECORD_KEY = "@ancestral_record@" const val ANCESTRAL_RECORD_KEY = "@ancestral_record@"
const val GLOBAL_METADATA_KEY = "@meta@" const val GLOBAL_METADATA_KEY = "@meta@"

View file

@ -17,7 +17,7 @@ data class BackupMetadata(
internal var time: Long = 0L, internal var time: Long = 0L,
internal val androidVersion: Int = Build.VERSION.SDK_INT, internal val androidVersion: Int = Build.VERSION.SDK_INT,
internal val androidIncremental: String = Build.VERSION.INCREMENTAL, internal val androidIncremental: String = Build.VERSION.INCREMENTAL,
internal val deviceName: String = "${Build.MANUFACTURER} ${Build.MODEL}", internal val deviceName: String = "D2D",
internal val packageMetadataMap: PackageMetadataMap = PackageMetadataMap(), internal val packageMetadataMap: PackageMetadataMap = PackageMetadataMap(),
) )

View file

@ -15,12 +15,16 @@ import android.content.Context
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.os.UserHandle
import android.os.UserManager
import android.util.Log import android.util.Log
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE import androidx.annotation.VisibleForTesting.PRIVATE
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import com.stevesoltys.seedvault.Clock import com.stevesoltys.seedvault.Clock
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
import com.stevesoltys.seedvault.PLATFORM_PACKAGE_NAME
import com.stevesoltys.seedvault.SHARED_BACKUP_AGENT_PACKAGE
import com.stevesoltys.seedvault.metadata.BackupType import com.stevesoltys.seedvault.metadata.BackupType
import com.stevesoltys.seedvault.metadata.MetadataManager import com.stevesoltys.seedvault.metadata.MetadataManager
import com.stevesoltys.seedvault.metadata.PackageState import com.stevesoltys.seedvault.metadata.PackageState
@ -143,12 +147,26 @@ internal class BackupCoordinator(
@Suppress("UNUSED_PARAMETER") isFullBackup: Boolean, @Suppress("UNUSED_PARAMETER") isFullBackup: Boolean,
): Boolean { ): Boolean {
val packageName = targetPackage.packageName val packageName = targetPackage.packageName
val applicationInfo = targetPackage.applicationInfo
// This must be kept in sync with frameworks/base/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
if (!targetPackage.allowsBackup(context, UserHandle.myUserId())
|| (UserHandle.isCore(applicationInfo.uid) &&
(applicationInfo.backupAgentName == null ||
(!UserManager.get(context).isSystemUser &&
(packageName == MAGIC_PACKAGE_MANAGER
|| packageName == PLATFORM_PACKAGE_NAME))))
|| packageName == SHARED_BACKUP_AGENT_PACKAGE
|| applicationInfo.isInstantApp
|| targetPackage.isStopped()
|| !applicationInfo.enabled) {
return false
}
// Check that the app is not blacklisted by the user // Check that the app is not blacklisted by the user
val enabled = settingsManager.isBackupEnabled(packageName) val enabled = settingsManager.isBackupEnabled(packageName)
if (!enabled) Log.w(TAG, "Backup of $packageName disabled by user.") if (!enabled) Log.w(TAG, "Backup of $packageName disabled by user.")
// We need to exclude the DocumentsProvider used to store backup data. // We need to exclude the DocumentsProvider used to store backup data.
// Otherwise, it gets killed when we back it up, terminating our backup. // Otherwise, it gets killed when we back it up, terminating our backup.
return enabled && targetPackage.packageName != plugin.providerPackageName return enabled && packageName != plugin.providerPackageName
} }
/** /**

View file

@ -7,8 +7,7 @@ val backupModule = module {
single { InputFactory() } single { InputFactory() }
single { single {
PackageService( PackageService(
context = androidContext(), context = androidContext()
backupManager = get()
) )
} }
single { single {

View file

@ -1,6 +1,7 @@
package com.stevesoltys.seedvault.transport.backup package com.stevesoltys.seedvault.transport.backup
import android.app.backup.IBackupManager import android.Manifest
import android.app.compat.CompatChanges
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
@ -22,13 +23,15 @@ private val TAG = PackageService::class.java.simpleName
private const val LOG_MAX_PACKAGES = 100 private const val LOG_MAX_PACKAGES = 100
// This must be kept in sync with frameworks/base/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
private const val IGNORE_ALLOW_BACKUP_IN_D2D = 183147249L
/** /**
* @author Steve Soltys * @author Steve Soltys
* @author Torsten Grote * @author Torsten Grote
*/ */
internal class PackageService( internal class PackageService(
private val context: Context, private val context: Context,
private val backupManager: IBackupManager,
) { ) {
private val packageManager: PackageManager = context.packageManager private val packageManager: PackageManager = context.packageManager
@ -50,17 +53,14 @@ internal class PackageService(
} }
} }
val eligibleApps = // val eligiblePackages = packages.filter { packageInfo ->
backupManager.filterAppsEligibleForBackupForUser(myUserId, packages.toTypedArray()) // isAppEligibleForBackup(packageInfo,
// if (packageInfo.applicationInfo.backupAgentName != null)
// log eligible packages // pkg.applicationInfo.flags and FLAG_FULL_BACKUP_ONLY != 0 else true)
if (Log.isLoggable(TAG, INFO)) { // }
Log.i(TAG, "Filtering left ${eligibleApps.size} eligible packages:")
logPackages(eligibleApps.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 = packages.toMutableList()
packageArray.add(MAGIC_PACKAGE_MANAGER) packageArray.add(MAGIC_PACKAGE_MANAGER)
return packageArray.toTypedArray() return packageArray.toTypedArray()
@ -73,7 +73,7 @@ internal class PackageService(
// because the package info is used by [ApkBackup] which needs signing info. // because the package info is used by [ApkBackup] which needs signing info.
return packageManager.getInstalledPackages(GET_SIGNING_CERTIFICATES) return packageManager.getInstalledPackages(GET_SIGNING_CERTIFICATES)
.filter { packageInfo -> .filter { packageInfo ->
packageInfo.doesNotGetBackedUp() && // only apps that do not allow backup packageInfo.doesNotGetBackedUp(context, myUserId) && // only apps that do not allow backup
!packageInfo.isNotUpdatedSystemApp() && // and are not vanilla system apps !packageInfo.isNotUpdatedSystemApp() && // and are not vanilla system apps
packageInfo.packageName != context.packageName // not this app packageInfo.packageName != context.packageName // not this app
}.sortedBy { packageInfo -> }.sortedBy { packageInfo ->
@ -94,7 +94,7 @@ internal class PackageService(
val userApps: List<PackageInfo> val userApps: List<PackageInfo>
@WorkerThread @WorkerThread
get() = packageManager.getInstalledPackages(GET_INSTRUMENTATION).filter { packageInfo -> get() = packageManager.getInstalledPackages(GET_INSTRUMENTATION).filter { packageInfo ->
packageInfo.isUserVisible(context) && packageInfo.allowsBackup() packageInfo.isUserVisible(context) && packageInfo.allowsBackup(context, myUserId)
} }
/** /**
@ -103,7 +103,7 @@ internal class PackageService(
val userNotAllowedApps: List<PackageInfo> val userNotAllowedApps: List<PackageInfo>
@WorkerThread @WorkerThread
get() = packageManager.getInstalledPackages(0).filter { packageInfo -> get() = packageManager.getInstalledPackages(0).filter { packageInfo ->
!packageInfo.allowsBackup() && !packageInfo.isSystemApp() !packageInfo.allowsBackup(context, myUserId) && !packageInfo.isSystemApp()
} }
val expectedAppTotals: ExpectedAppTotals val expectedAppTotals: ExpectedAppTotals
@ -114,7 +114,7 @@ internal class PackageService(
packageManager.getInstalledPackages(GET_INSTRUMENTATION).forEach { packageInfo -> packageManager.getInstalledPackages(GET_INSTRUMENTATION).forEach { packageInfo ->
if (packageInfo.isUserVisible(context)) { if (packageInfo.isUserVisible(context)) {
appsTotal++ appsTotal++
if (packageInfo.doesNotGetBackedUp()) { if (packageInfo.doesNotGetBackedUp(context, myUserId)) {
appsOptOut++ appsOptOut++
} }
} }
@ -157,9 +157,20 @@ internal fun PackageInfo.isSystemApp(): Boolean {
return applicationInfo.flags and FLAG_SYSTEM != 0 return applicationInfo.flags and FLAG_SYSTEM != 0
} }
internal fun PackageInfo.allowsBackup(): Boolean { internal fun PackageInfo.allowsBackup(context: Context, userId: Int): 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
// This must be kept in sync with frameworks/base/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
val isSystemApp = applicationInfo.flags and FLAG_SYSTEM != 0
val ignoreAllowBackup = !isSystemApp
&& (context.checkSelfPermission(Manifest.permission.LOG_COMPAT_CHANGE)
== PackageManager.PERMISSION_GRANTED)
&& (context.checkSelfPermission(Manifest.permission.READ_COMPAT_CHANGE_CONFIG)
== PackageManager.PERMISSION_GRANTED)
&& CompatChanges.isChangeEnabled(IGNORE_ALLOW_BACKUP_IN_D2D, packageName,
UserHandle.of(userId))
val allowBackup = applicationInfo.flags and FLAG_ALLOW_BACKUP != 0
return ignoreAllowBackup || allowBackup
} }
/** /**
@ -173,10 +184,21 @@ internal fun PackageInfo.isNotUpdatedSystemApp(): Boolean {
return isSystemApp && !isUpdatedSystemApp return isSystemApp && !isUpdatedSystemApp
} }
internal fun PackageInfo.doesNotGetBackedUp(): Boolean { internal fun PackageInfo.doesNotGetBackedUp(context: Context, userId: Int): Boolean {
if (packageName == MAGIC_PACKAGE_MANAGER || applicationInfo == null) return true if (packageName == MAGIC_PACKAGE_MANAGER || applicationInfo == null) return true
return applicationInfo.flags and FLAG_ALLOW_BACKUP == 0 || // does not allow backup
applicationInfo.flags and FLAG_STOPPED != 0 // is stopped // This must be kept in sync with frameworks/base/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
val isSystemApp = applicationInfo.flags and FLAG_SYSTEM != 0
val ignoreAllowBackup = !isSystemApp
&& (context.checkSelfPermission(Manifest.permission.LOG_COMPAT_CHANGE)
== PackageManager.PERMISSION_GRANTED)
&& (context.checkSelfPermission(Manifest.permission.READ_COMPAT_CHANGE_CONFIG)
== PackageManager.PERMISSION_GRANTED)
&& CompatChanges.isChangeEnabled(IGNORE_ALLOW_BACKUP_IN_D2D, packageName,
UserHandle.of(userId))
val allowBackup = applicationInfo.flags and FLAG_ALLOW_BACKUP != 0
val stopped = applicationInfo.flags and FLAG_STOPPED != 0
return (!ignoreAllowBackup && !allowBackup) || stopped
} }
internal fun PackageInfo.isStopped(): Boolean { internal fun PackageInfo.isStopped(): Boolean {

View file

@ -5,6 +5,8 @@
<permission name="android.permission.MANAGE_USB"/> <permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.INSTALL_PACKAGES"/> <permission name="android.permission.INSTALL_PACKAGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<permission name="android.permission.LOG_COMPAT_CHANGE"/>
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<permission name="android.permission.POST_NOTIFICATIONS"/> <permission name="android.permission.POST_NOTIFICATIONS"/>