Add support for d2d
Change-Id: I61d88a511a0f81e6d384e3650f6797725da79814
This commit is contained in:
parent
72c8460864
commit
4a98667162
7 changed files with 73 additions and 25 deletions
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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@"
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ val backupModule = module {
|
||||||
single { InputFactory() }
|
single { InputFactory() }
|
||||||
single {
|
single {
|
||||||
PackageService(
|
PackageService(
|
||||||
context = androidContext(),
|
context = androidContext()
|
||||||
backupManager = get()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
single {
|
single {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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"/>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue