Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21f45cd9 authored by Ruslan Tkhakokhov's avatar Ruslan Tkhakokhov
Browse files

Handle empty <cloud-backup> section in android:dataExtractionRules

Currently the <cloud-backup> 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 <cloud-backup> 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
parent 20c38783
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -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<PathWithRequiredFlags> excludes,
                Map<String, Set<PathWithRequiredFlags>> 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,
+33 −0
Original line number Diff line number Diff line
@@ -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(
                "<data-extraction-rules>" +
                        "<cloud-backup>" +
                        "</cloud-backup>" +
                        "</data-extraction-rules>"));

        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(
                "<data-extraction-rules>" +
                        "<cloud-backup disableIfNoEncryptionCapabilities=\"true\">" +
                        "</cloud-backup>" +
                        "</data-extraction-rules>"));

        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(
                "<full-backup-content>" +