From 21f45cd998192942da3428ce8439689097af0aa2 Mon Sep 17 00:00:00 2001 From: Ruslan Tkhakokhov Date: Wed, 4 Aug 2021 09:01:23 +0100 Subject: [PATCH] Handle empty section in android:dataExtractionRules Currently the section in android:dataExtractionRules is ignored unless it contains rules. Instead, we should interpret it as 'everything other than cache and no-backup dirs is eligible for cloud backup'. Bug: 195095045 Test: 1. atest BackupEligibilityHostSideTest 2. Use a test app with empty section to manually test: 2.1. Empty section - everything is backed up 2.2. Empty section but "disableIfNoEncryptionCapabilitites" set to "true" - data only backed up if the transport supports encryption. Change-Id: Ic8066721a46bda688f9211c51a0f2497e9caf93b --- core/java/android/app/backup/FullBackup.java | 22 +++++++++---- .../android/app/backup/FullBackupTest.java | 33 +++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 1b03f2f328e5f..bf9a9b060b6b1 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -116,7 +116,7 @@ public class FullBackup { ConfigSection.CLOUD_BACKUP, ConfigSection.DEVICE_TRANSFER }) - private @interface ConfigSection { + @interface ConfigSection { String CLOUD_BACKUP = "cloud-backup"; String DEVICE_TRANSFER = "device-transfer"; } @@ -528,7 +528,8 @@ public class FullBackup { return mExcludes; } - private synchronized int getRequiredTransportFlags() + @VisibleForTesting + public synchronized int getRequiredTransportFlags() throws IOException, XmlPullParserException { if (mRequiredTransportFlags == null) { maybeParseBackupSchemeLocked(); @@ -587,11 +588,13 @@ public class FullBackup { if (mDataExtractionRules != 0) { // New config is present. Use it if it has configuration for this operation // type. + boolean isSectionPresent; try (XmlResourceParser parser = getParserForResource(mDataExtractionRules)) { - parseNewBackupSchemeFromXmlLocked(parser, configSection, mExcludes, mIncludes); + isSectionPresent = parseNewBackupSchemeFromXmlLocked(parser, configSection, + mExcludes, mIncludes); } - if (!mExcludes.isEmpty() || !mIncludes.isEmpty()) { - // Found configuration in the new config, we will use it. + if (isSectionPresent) { + // Found the relevant section in the new config, we will use it. mIsUsingNewScheme = true; return; } @@ -630,24 +633,31 @@ public class FullBackup { .getXml(resourceId); } - private void parseNewBackupSchemeFromXmlLocked(XmlPullParser parser, + @VisibleForTesting + public boolean parseNewBackupSchemeFromXmlLocked(XmlPullParser parser, @ConfigSection String configSection, Set excludes, Map> includes) throws IOException, XmlPullParserException { verifyTopLevelTag(parser, "data-extraction-rules"); + boolean isSectionPresent = false; + int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event != XmlPullParser.START_TAG || !configSection.equals(parser.getName())) { continue; } + isSectionPresent = true; + parseRequiredTransportFlags(parser, configSection); parseRules(parser, excludes, includes, Optional.of(0), configSection); } logParsingResults(excludes, includes); + + return isSectionPresent; } private void parseRequiredTransportFlags(XmlPullParser parser, diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java index 08edb4e9be97b..bc92da928d1ea 100644 --- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java +++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java @@ -16,6 +16,8 @@ package android.app.backup; +import static android.app.backup.FullBackup.ConfigSection.CLOUD_BACKUP; + import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; import android.content.Context; import android.test.AndroidTestCase; @@ -414,6 +416,37 @@ public class FullBackupTest extends AndroidTestCase { assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes); } + public void testParseNewBackupSchemeFromXml_emptyCloudSectionIsRespected() throws Exception { + mXpp.setInput(new StringReader( + "" + + "" + + "" + + "")); + + FullBackup.BackupScheme backupScheme = FullBackup.getBackupSchemeForTest(mContext); + boolean result = backupScheme.parseNewBackupSchemeFromXmlLocked(mXpp, CLOUD_BACKUP, + excludesSet, includeMap); + + assertTrue(result); + } + + public void testParseNewBackupSchemeFromXml_emptyCloudSectionWithEncryptionFlagIsRespected() + throws Exception { + mXpp.setInput(new StringReader( + "" + + "" + + "" + + "")); + + FullBackup.BackupScheme backupScheme = FullBackup.getBackupSchemeForTest(mContext); + boolean result = backupScheme.parseNewBackupSchemeFromXmlLocked(mXpp, CLOUD_BACKUP, + excludesSet, includeMap); + + assertTrue(result); + assertEquals(backupScheme.getRequiredTransportFlags(), + BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED); + } + public void testDoubleDotInPath_isIgnored() throws Exception { mXpp.setInput(new StringReader( "" +