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"
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
android:name=".App"
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.IBackupManager
import android.content.Context
import android.content.Context.BACKUP_SERVICE
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Build
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 ANCESTRAL_RECORD_KEY = "@ancestral_record@"
const val GLOBAL_METADATA_KEY = "@meta@"

View file

@ -17,7 +17,7 @@ data class BackupMetadata(
internal var time: Long = 0L,
internal val androidVersion: Int = Build.VERSION.SDK_INT,
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(),
)

View file

@ -15,12 +15,16 @@ import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
import android.os.ParcelFileDescriptor
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE
import androidx.annotation.WorkerThread
import com.stevesoltys.seedvault.Clock
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.MetadataManager
import com.stevesoltys.seedvault.metadata.PackageState
@ -143,12 +147,26 @@ internal class BackupCoordinator(
@Suppress("UNUSED_PARAMETER") isFullBackup: Boolean,
): Boolean {
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
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
return enabled && packageName != plugin.providerPackageName
}
/**

View file

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

View file

@ -1,6 +1,7 @@
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.pm.ApplicationInfo.FLAG_ALLOW_BACKUP
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
// 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 Torsten Grote
*/
internal class PackageService(
private val context: Context,
private val backupManager: IBackupManager,
) {
private val packageManager: PackageManager = context.packageManager
@ -50,17 +53,14 @@ internal class PackageService(
}
}
val eligibleApps =
backupManager.filterAppsEligibleForBackupForUser(myUserId, packages.toTypedArray())
// log eligible packages
if (Log.isLoggable(TAG, INFO)) {
Log.i(TAG, "Filtering left ${eligibleApps.size} eligible packages:")
logPackages(eligibleApps.toList())
}
// val eligiblePackages = packages.filter { packageInfo ->
// isAppEligibleForBackup(packageInfo,
// if (packageInfo.applicationInfo.backupAgentName != null)
// pkg.applicationInfo.flags and FLAG_FULL_BACKUP_ONLY != 0 else true)
// }
// 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)
return packageArray.toTypedArray()
@ -73,7 +73,7 @@ internal class PackageService(
// because the package info is used by [ApkBackup] which needs signing info.
return packageManager.getInstalledPackages(GET_SIGNING_CERTIFICATES)
.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.packageName != context.packageName // not this app
}.sortedBy { packageInfo ->
@ -94,7 +94,7 @@ internal class PackageService(
val userApps: List<PackageInfo>
@WorkerThread
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>
@WorkerThread
get() = packageManager.getInstalledPackages(0).filter { packageInfo ->
!packageInfo.allowsBackup() && !packageInfo.isSystemApp()
!packageInfo.allowsBackup(context, myUserId) && !packageInfo.isSystemApp()
}
val expectedAppTotals: ExpectedAppTotals
@ -114,7 +114,7 @@ internal class PackageService(
packageManager.getInstalledPackages(GET_INSTRUMENTATION).forEach { packageInfo ->
if (packageInfo.isUserVisible(context)) {
appsTotal++
if (packageInfo.doesNotGetBackedUp()) {
if (packageInfo.doesNotGetBackedUp(context, myUserId)) {
appsOptOut++
}
}
@ -157,9 +157,20 @@ internal fun PackageInfo.isSystemApp(): Boolean {
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
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
}
internal fun PackageInfo.doesNotGetBackedUp(): Boolean {
internal fun PackageInfo.doesNotGetBackedUp(context: Context, userId: Int): Boolean {
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 {

View file

@ -5,6 +5,8 @@
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
<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.MANAGE_EXTERNAL_STORAGE"/>
<permission name="android.permission.POST_NOTIFICATIONS"/>