Loading core/java/android/app/backup/BackupAgent.java +19 −41 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package android.app.backup; import android.app.IBackupAgent; import android.app.QueuedWork; import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; Loading Loading @@ -344,8 +343,8 @@ public abstract class BackupAgent extends ContextWrapper { return; } Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap; ArraySet<PathWithRequiredFlags> manifestExcludeSet; Map<String, Set<String>> manifestIncludeMap; ArraySet<String> manifestExcludeSet; try { manifestIncludeMap = backupScheme.maybeParseAndGetCanonicalIncludePaths(); Loading Loading @@ -515,13 +514,14 @@ public abstract class BackupAgent extends ContextWrapper { /** * Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>. * If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path * is a directory, but only if all the required flags of the include rule are satisfied by * the transport. * is a directory. */ private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken, Map<String, Set<PathWithRequiredFlags>> includeMap, ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet, FullBackupDataOutput data) throws IOException { Map<String, Set<String>> includeMap, ArraySet<String> filterSet, ArraySet<String> traversalExcludeSet, FullBackupDataOutput data) throws IOException { if (includeMap == null || includeMap.size() == 0) { // Do entire sub-tree for the provided token. fullBackupFileTree(packageName, domainToken, Loading @@ -530,21 +530,12 @@ public abstract class BackupAgent extends ContextWrapper { } else if (includeMap.get(domainToken) != null) { // This will be null if the xml parsing didn't yield any rules for // this domain (there may still be rules for other domains). for (PathWithRequiredFlags includeFile : includeMap.get(domainToken)) { if (areIncludeRequiredTransportFlagsSatisfied(includeFile.getRequiredFlags(), data.getTransportFlags())) { fullBackupFileTree(packageName, domainToken, includeFile.getPath(), filterSet, for (String includeFile : includeMap.get(domainToken)) { fullBackupFileTree(packageName, domainToken, includeFile, filterSet, traversalExcludeSet, data); } } } } private boolean areIncludeRequiredTransportFlagsSatisfied(int includeFlags, int transportFlags) { // all bits that are set in includeFlags must also be set in transportFlags return (transportFlags & includeFlags) == includeFlags; } /** * Write an entire file as part of a full-backup operation. The file's contents Loading Loading @@ -692,7 +683,7 @@ public abstract class BackupAgent extends ContextWrapper { * @hide */ protected final void fullBackupFileTree(String packageName, String domain, String startingPath, ArraySet<PathWithRequiredFlags> manifestExcludes, ArraySet<String> manifestExcludes, ArraySet<String> systemExcludes, FullBackupDataOutput output) { // Pull out the domain and set it aside to use when making the tarball. Loading Loading @@ -723,8 +714,7 @@ public abstract class BackupAgent extends ContextWrapper { filePath = file.getCanonicalPath(); // prune this subtree? if (manifestExcludes != null && manifestExcludesContainFilePath(manifestExcludes, filePath)) { if (manifestExcludes != null && manifestExcludes.contains(filePath)) { continue; } if (systemExcludes != null && systemExcludes.contains(filePath)) { Loading Loading @@ -760,17 +750,6 @@ public abstract class BackupAgent extends ContextWrapper { } } private boolean manifestExcludesContainFilePath( ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) { for (PathWithRequiredFlags exclude : manifestExcludes) { String excludePath = exclude.getPath(); if (excludePath != null && excludePath.equals(filePath)) { return true; } } return false; } /** * Handle the data delivered via the given file descriptor during a full restore * operation. The agent is given the path to the file's original location as well Loading Loading @@ -817,8 +796,8 @@ public abstract class BackupAgent extends ContextWrapper { return false; } Map<String, Set<PathWithRequiredFlags>> includes = null; ArraySet<PathWithRequiredFlags> excludes = null; Map<String, Set<String>> includes = null; ArraySet<String> excludes = null; final String destinationCanonicalPath = destination.getCanonicalPath(); try { includes = bs.maybeParseAndGetCanonicalIncludePaths(); Loading Loading @@ -847,7 +826,7 @@ public abstract class BackupAgent extends ContextWrapper { // Rather than figure out the <include/> domain based on the path (a lot of code, and // it's a small list), we'll go through and look for it. boolean explicitlyIncluded = false; for (Set<PathWithRequiredFlags> domainIncludes : includes.values()) { for (Set<String> domainIncludes : includes.values()) { explicitlyIncluded |= isFileSpecifiedInPathList(destination, domainIncludes); if (explicitlyIncluded) { break; Loading @@ -870,10 +849,9 @@ public abstract class BackupAgent extends ContextWrapper { * @return True if the provided file is either directly in the provided list, or the provided * file is within a directory in the list. */ private boolean isFileSpecifiedInPathList(File file, Collection<PathWithRequiredFlags> canonicalPathList) throws IOException { for (PathWithRequiredFlags canonical : canonicalPathList) { String canonicalPath = canonical.getPath(); private boolean isFileSpecifiedInPathList(File file, Collection<String> canonicalPathList) throws IOException { for (String canonicalPath : canonicalPathList) { File fileFromList = new File(canonicalPath); if (fileFromList.isDirectory()) { if (file.isDirectory()) { Loading core/java/android/app/backup/FullBackup.java +51 −138 Original line number Diff line number Diff line Loading @@ -82,9 +82,6 @@ public class FullBackup { public static final String FULL_RESTORE_INTENT_ACTION = "fullrest"; public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken"; public static final String FLAG_REQUIRED_CLIENT_SIDE_ENCRYPTION = "clientSideEncryption"; public static final String FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER = "deviceToDeviceTransfer"; /** * @hide */ Loading Loading @@ -227,9 +224,6 @@ public class FullBackup { private final File EXTERNAL_DIR; private final static String TAG_INCLUDE = "include"; private final static String TAG_EXCLUDE = "exclude"; final int mFullBackupContent; final PackageManager mPackageManager; final StorageManager mStorageManager; Loading Loading @@ -309,45 +303,15 @@ public class FullBackup { } /** * Represents a path attribute specified in an <include /> rule along with optional * transport flags required from the transport to include file(s) under that path as * specified by requiredFlags attribute. If optional requiredFlags attribute is not * provided, default requiredFlags to 0. * Note: since our parsing codepaths were the same for <include /> and <exclude /> tags, * this structure is also used for <exclude /> tags to preserve that, however you can expect * the getRequiredFlags() to always return 0 for exclude rules. */ public static class PathWithRequiredFlags { private final String mPath; private final int mRequiredFlags; public PathWithRequiredFlags(String path, int requiredFlags) { mPath = path; mRequiredFlags = requiredFlags; } public String getPath() { return mPath; } public int getRequiredFlags() { return mRequiredFlags; } } /** * A map of domain -> set of pairs (canonical file; required transport flags) in that * domain that are to be included if the transport has decared the required flags. * A map of domain -> list of canonical file names in that domain that are to be included. * We keep track of the domain so that we can go through the file system in order later on. */ Map<String, Set<PathWithRequiredFlags>> mIncludes; /** * Set that will be populated with pairs (canonical file; requiredFlags=0) for each file or * directory that is to be excluded. Note that for excludes, the requiredFlags attribute is * ignored and the value should be always set to 0. Map<String, Set<String>> mIncludes; /**e * List that will be populated with the canonical names of each file or directory that is * to be excluded. */ ArraySet<PathWithRequiredFlags> mExcludes; ArraySet<String> mExcludes; BackupScheme(Context context) { mFullBackupContent = context.getApplicationInfo().fullBackupContent; Loading Loading @@ -392,14 +356,13 @@ public class FullBackup { } /** * @return A mapping of domain -> set of pairs (canonical file; required transport flags) * in that domain that are to be included if the transport has decared the required flags. * Each of these paths specifies a file that the client has explicitly included in their * backup set. If this map is empty we will back up the entire data directory (including * managed external storage). * @return A mapping of domain -> canonical paths within that domain. Each of these paths * specifies a file that the client has explicitly included in their backup set. If this * map is empty we will back up the entire data directory (including managed external * storage). */ public synchronized Map<String, Set<PathWithRequiredFlags>> maybeParseAndGetCanonicalIncludePaths() throws IOException, XmlPullParserException { public synchronized Map<String, Set<String>> maybeParseAndGetCanonicalIncludePaths() throws IOException, XmlPullParserException { if (mIncludes == null) { maybeParseBackupSchemeLocked(); } Loading @@ -407,10 +370,9 @@ public class FullBackup { } /** * @return A set of (canonical paths; requiredFlags=0) that are to be excluded from the * backup/restore set. * @return A set of canonical paths that are to be excluded from the backup/restore set. */ public synchronized ArraySet<PathWithRequiredFlags> maybeParseAndGetCanonicalExcludePaths() public synchronized ArraySet<String> maybeParseAndGetCanonicalExcludePaths() throws IOException, XmlPullParserException { if (mExcludes == null) { maybeParseBackupSchemeLocked(); Loading @@ -420,8 +382,8 @@ public class FullBackup { private void maybeParseBackupSchemeLocked() throws IOException, XmlPullParserException { // This not being null is how we know that we've tried to parse the xml already. mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>(); mExcludes = new ArraySet<PathWithRequiredFlags>(); mIncludes = new ArrayMap<String, Set<String>>(); mExcludes = new ArraySet<String>(); if (mFullBackupContent == 0) { // android:fullBackupContent="true" which means that we'll do everything. Loading Loading @@ -453,8 +415,8 @@ public class FullBackup { @VisibleForTesting public void parseBackupSchemeFromXmlLocked(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, Map<String, Set<PathWithRequiredFlags>> includes) Set<String> excludes, Map<String, Set<String>> includes) throws IOException, XmlPullParserException { int event = parser.getEventType(); // START_DOCUMENT while (event != XmlPullParser.START_TAG) { Loading @@ -479,7 +441,8 @@ public class FullBackup { case XmlPullParser.START_TAG: validateInnerTagContents(parser); final String domainFromXml = parser.getAttributeValue(null, "domain"); final File domainDirectory = getDirectoryForCriteriaDomain(domainFromXml); final File domainDirectory = getDirectoryForCriteriaDomain(domainFromXml); if (domainDirectory == null) { if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...parsing \"" + parser.getName() + "\": " Loading @@ -494,23 +457,12 @@ public class FullBackup { break; } int requiredFlags = 0; // no transport flags are required by default if (TAG_INCLUDE.equals(parser.getName())) { // requiredFlags are only supported for <include /> tag, for <exclude /> // we should always leave them as the default = 0 requiredFlags = getRequiredFlagsFromString( parser.getAttributeValue(null, "requireFlags")); } // retrieve the include/exclude set we'll be adding this rule to Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain( Set<String> activeSet = parseCurrentTagForDomain( parser, excludes, includes, domainFromXml); activeSet.add(new PathWithRequiredFlags(canonicalFile.getCanonicalPath(), requiredFlags)); activeSet.add(canonicalFile.getCanonicalPath()); if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...parsed " + canonicalFile.getCanonicalPath() + " for domain \"" + domainFromXml + "\", requiredFlags + \"" + requiredFlags + "\""); + " for domain \"" + domainFromXml + "\""); } // Special case journal files (not dirs) for sqlite database. frowny-face. Loading @@ -520,16 +472,14 @@ public class FullBackup { if ("database".equals(domainFromXml) && !canonicalFile.isDirectory()) { final String canonicalJournalPath = canonicalFile.getCanonicalPath() + "-journal"; activeSet.add(new PathWithRequiredFlags(canonicalJournalPath, requiredFlags)); activeSet.add(canonicalJournalPath); if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...automatically generated " + canonicalJournalPath + ". Ignore if nonexistent."); } final String canonicalWalPath = canonicalFile.getCanonicalPath() + "-wal"; activeSet.add(new PathWithRequiredFlags(canonicalWalPath, requiredFlags)); activeSet.add(canonicalWalPath); if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...automatically generated " + canonicalWalPath + ". Ignore if nonexistent."); Loading @@ -541,8 +491,7 @@ public class FullBackup { !canonicalFile.getCanonicalPath().endsWith(".xml")) { final String canonicalXmlPath = canonicalFile.getCanonicalPath() + ".xml"; activeSet.add(new PathWithRequiredFlags(canonicalXmlPath, requiredFlags)); activeSet.add(canonicalXmlPath); if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...automatically generated " + canonicalXmlPath + ". Ignore if nonexistent."); Loading @@ -559,12 +508,10 @@ public class FullBackup { Log.v(TAG_XML_PARSER, " ...nothing specified (This means the entirety of app" + " data minus excludes)"); } else { for (Map.Entry<String, Set<PathWithRequiredFlags>> entry : includes.entrySet()) { for (Map.Entry<String, Set<String>> entry : includes.entrySet()) { Log.v(TAG_XML_PARSER, " domain=" + entry.getKey()); for (PathWithRequiredFlags includeData : entry.getValue()) { Log.v(TAG_XML_PARSER, " path: " + includeData.getPath() + " requiredFlags: " + includeData.getRequiredFlags()); for (String includeData : entry.getValue()) { Log.v(TAG_XML_PARSER, " " + includeData); } } } Loading @@ -573,9 +520,8 @@ public class FullBackup { if (excludes.isEmpty()) { Log.v(TAG_XML_PARSER, " ...nothing to exclude."); } else { for (PathWithRequiredFlags excludeData : excludes) { Log.v(TAG_XML_PARSER, " path: " + excludeData.getPath() + " requiredFlags: " + excludeData.getRequiredFlags()); for (String excludeData : excludes) { Log.v(TAG_XML_PARSER, " " + excludeData); } } Loading @@ -585,41 +531,20 @@ public class FullBackup { } } private int getRequiredFlagsFromString(String requiredFlags) { int flags = 0; if (requiredFlags == null || requiredFlags.length() == 0) { // requiredFlags attribute was missing or empty in <include /> tag return flags; } String[] flagsStr = requiredFlags.split("\\|"); for (String f : flagsStr) { switch (f) { case FLAG_REQUIRED_CLIENT_SIDE_ENCRYPTION: flags |= BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED; break; case FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER: flags |= BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER; break; default: Log.w(TAG, "Unrecognized requiredFlag provided, value: \"" + f + "\""); } } return flags; } private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, Map<String, Set<PathWithRequiredFlags>> includes, String domain) private Set<String> parseCurrentTagForDomain(XmlPullParser parser, Set<String> excludes, Map<String, Set<String>> includes, String domain) throws XmlPullParserException { if (TAG_INCLUDE.equals(parser.getName())) { if ("include".equals(parser.getName())) { final String domainToken = getTokenForXmlDomain(domain); Set<PathWithRequiredFlags> includeSet = includes.get(domainToken); Set<String> includeSet = includes.get(domainToken); if (includeSet == null) { includeSet = new ArraySet<PathWithRequiredFlags>(); includeSet = new ArraySet<String>(); includes.put(domainToken, includeSet); } return includeSet; } else if (TAG_EXCLUDE.equals(parser.getName())) { } else if ("exclude".equals(parser.getName())) { return excludes; } else { // Unrecognised tag => hard failure. Loading Loading @@ -664,8 +589,8 @@ public class FullBackup { /** * * @param domain Directory where the specified file should exist. Not null. * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may * be null. * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may be * null. * @return The canonical path of the file specified or null if no such file exists. */ private File extractCanonicalFile(File domain, String filePathFromXml) { Loading Loading @@ -725,25 +650,13 @@ public class FullBackup { * Let's be strict about the type of xml the client can write. If we see anything untoward, * throw an XmlPullParserException. */ private void validateInnerTagContents(XmlPullParser parser) throws XmlPullParserException { if (parser == null) { return; } switch (parser.getName()) { case TAG_INCLUDE: if (parser.getAttributeCount() > 3) { throw new XmlPullParserException("At most 3 tag attributes allowed for " + "\"include\" tag (\"domain\" & \"path\"" + " & optional \"requiredFlags\")."); } break; case TAG_EXCLUDE: private void validateInnerTagContents(XmlPullParser parser) throws XmlPullParserException { if (parser.getAttributeCount() > 2) { throw new XmlPullParserException("At most 2 tag attributes allowed for " + "\"exclude\" tag (\"domain\" & \"path\"."); throw new XmlPullParserException("At most 2 tag attributes allowed for \"" + parser.getName() + "\" tag (\"domain\" & \"path\"."); } break; default: if (!"include".equals(parser.getName()) && !"exclude".equals(parser.getName())) { throw new XmlPullParserException("A valid tag is one of \"<include/>\" or" + " \"<exclude/>. You provided \"" + parser.getName() + "\""); } Loading core/tests/coretests/src/android/app/backup/FullBackupTest.java +26 −157 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/backup/BackupAgent.java +19 −41 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package android.app.backup; import android.app.IBackupAgent; import android.app.QueuedWork; import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; Loading Loading @@ -344,8 +343,8 @@ public abstract class BackupAgent extends ContextWrapper { return; } Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap; ArraySet<PathWithRequiredFlags> manifestExcludeSet; Map<String, Set<String>> manifestIncludeMap; ArraySet<String> manifestExcludeSet; try { manifestIncludeMap = backupScheme.maybeParseAndGetCanonicalIncludePaths(); Loading Loading @@ -515,13 +514,14 @@ public abstract class BackupAgent extends ContextWrapper { /** * Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>. * If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path * is a directory, but only if all the required flags of the include rule are satisfied by * the transport. * is a directory. */ private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken, Map<String, Set<PathWithRequiredFlags>> includeMap, ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet, FullBackupDataOutput data) throws IOException { Map<String, Set<String>> includeMap, ArraySet<String> filterSet, ArraySet<String> traversalExcludeSet, FullBackupDataOutput data) throws IOException { if (includeMap == null || includeMap.size() == 0) { // Do entire sub-tree for the provided token. fullBackupFileTree(packageName, domainToken, Loading @@ -530,21 +530,12 @@ public abstract class BackupAgent extends ContextWrapper { } else if (includeMap.get(domainToken) != null) { // This will be null if the xml parsing didn't yield any rules for // this domain (there may still be rules for other domains). for (PathWithRequiredFlags includeFile : includeMap.get(domainToken)) { if (areIncludeRequiredTransportFlagsSatisfied(includeFile.getRequiredFlags(), data.getTransportFlags())) { fullBackupFileTree(packageName, domainToken, includeFile.getPath(), filterSet, for (String includeFile : includeMap.get(domainToken)) { fullBackupFileTree(packageName, domainToken, includeFile, filterSet, traversalExcludeSet, data); } } } } private boolean areIncludeRequiredTransportFlagsSatisfied(int includeFlags, int transportFlags) { // all bits that are set in includeFlags must also be set in transportFlags return (transportFlags & includeFlags) == includeFlags; } /** * Write an entire file as part of a full-backup operation. The file's contents Loading Loading @@ -692,7 +683,7 @@ public abstract class BackupAgent extends ContextWrapper { * @hide */ protected final void fullBackupFileTree(String packageName, String domain, String startingPath, ArraySet<PathWithRequiredFlags> manifestExcludes, ArraySet<String> manifestExcludes, ArraySet<String> systemExcludes, FullBackupDataOutput output) { // Pull out the domain and set it aside to use when making the tarball. Loading Loading @@ -723,8 +714,7 @@ public abstract class BackupAgent extends ContextWrapper { filePath = file.getCanonicalPath(); // prune this subtree? if (manifestExcludes != null && manifestExcludesContainFilePath(manifestExcludes, filePath)) { if (manifestExcludes != null && manifestExcludes.contains(filePath)) { continue; } if (systemExcludes != null && systemExcludes.contains(filePath)) { Loading Loading @@ -760,17 +750,6 @@ public abstract class BackupAgent extends ContextWrapper { } } private boolean manifestExcludesContainFilePath( ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) { for (PathWithRequiredFlags exclude : manifestExcludes) { String excludePath = exclude.getPath(); if (excludePath != null && excludePath.equals(filePath)) { return true; } } return false; } /** * Handle the data delivered via the given file descriptor during a full restore * operation. The agent is given the path to the file's original location as well Loading Loading @@ -817,8 +796,8 @@ public abstract class BackupAgent extends ContextWrapper { return false; } Map<String, Set<PathWithRequiredFlags>> includes = null; ArraySet<PathWithRequiredFlags> excludes = null; Map<String, Set<String>> includes = null; ArraySet<String> excludes = null; final String destinationCanonicalPath = destination.getCanonicalPath(); try { includes = bs.maybeParseAndGetCanonicalIncludePaths(); Loading Loading @@ -847,7 +826,7 @@ public abstract class BackupAgent extends ContextWrapper { // Rather than figure out the <include/> domain based on the path (a lot of code, and // it's a small list), we'll go through and look for it. boolean explicitlyIncluded = false; for (Set<PathWithRequiredFlags> domainIncludes : includes.values()) { for (Set<String> domainIncludes : includes.values()) { explicitlyIncluded |= isFileSpecifiedInPathList(destination, domainIncludes); if (explicitlyIncluded) { break; Loading @@ -870,10 +849,9 @@ public abstract class BackupAgent extends ContextWrapper { * @return True if the provided file is either directly in the provided list, or the provided * file is within a directory in the list. */ private boolean isFileSpecifiedInPathList(File file, Collection<PathWithRequiredFlags> canonicalPathList) throws IOException { for (PathWithRequiredFlags canonical : canonicalPathList) { String canonicalPath = canonical.getPath(); private boolean isFileSpecifiedInPathList(File file, Collection<String> canonicalPathList) throws IOException { for (String canonicalPath : canonicalPathList) { File fileFromList = new File(canonicalPath); if (fileFromList.isDirectory()) { if (file.isDirectory()) { Loading
core/java/android/app/backup/FullBackup.java +51 −138 Original line number Diff line number Diff line Loading @@ -82,9 +82,6 @@ public class FullBackup { public static final String FULL_RESTORE_INTENT_ACTION = "fullrest"; public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken"; public static final String FLAG_REQUIRED_CLIENT_SIDE_ENCRYPTION = "clientSideEncryption"; public static final String FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER = "deviceToDeviceTransfer"; /** * @hide */ Loading Loading @@ -227,9 +224,6 @@ public class FullBackup { private final File EXTERNAL_DIR; private final static String TAG_INCLUDE = "include"; private final static String TAG_EXCLUDE = "exclude"; final int mFullBackupContent; final PackageManager mPackageManager; final StorageManager mStorageManager; Loading Loading @@ -309,45 +303,15 @@ public class FullBackup { } /** * Represents a path attribute specified in an <include /> rule along with optional * transport flags required from the transport to include file(s) under that path as * specified by requiredFlags attribute. If optional requiredFlags attribute is not * provided, default requiredFlags to 0. * Note: since our parsing codepaths were the same for <include /> and <exclude /> tags, * this structure is also used for <exclude /> tags to preserve that, however you can expect * the getRequiredFlags() to always return 0 for exclude rules. */ public static class PathWithRequiredFlags { private final String mPath; private final int mRequiredFlags; public PathWithRequiredFlags(String path, int requiredFlags) { mPath = path; mRequiredFlags = requiredFlags; } public String getPath() { return mPath; } public int getRequiredFlags() { return mRequiredFlags; } } /** * A map of domain -> set of pairs (canonical file; required transport flags) in that * domain that are to be included if the transport has decared the required flags. * A map of domain -> list of canonical file names in that domain that are to be included. * We keep track of the domain so that we can go through the file system in order later on. */ Map<String, Set<PathWithRequiredFlags>> mIncludes; /** * Set that will be populated with pairs (canonical file; requiredFlags=0) for each file or * directory that is to be excluded. Note that for excludes, the requiredFlags attribute is * ignored and the value should be always set to 0. Map<String, Set<String>> mIncludes; /**e * List that will be populated with the canonical names of each file or directory that is * to be excluded. */ ArraySet<PathWithRequiredFlags> mExcludes; ArraySet<String> mExcludes; BackupScheme(Context context) { mFullBackupContent = context.getApplicationInfo().fullBackupContent; Loading Loading @@ -392,14 +356,13 @@ public class FullBackup { } /** * @return A mapping of domain -> set of pairs (canonical file; required transport flags) * in that domain that are to be included if the transport has decared the required flags. * Each of these paths specifies a file that the client has explicitly included in their * backup set. If this map is empty we will back up the entire data directory (including * managed external storage). * @return A mapping of domain -> canonical paths within that domain. Each of these paths * specifies a file that the client has explicitly included in their backup set. If this * map is empty we will back up the entire data directory (including managed external * storage). */ public synchronized Map<String, Set<PathWithRequiredFlags>> maybeParseAndGetCanonicalIncludePaths() throws IOException, XmlPullParserException { public synchronized Map<String, Set<String>> maybeParseAndGetCanonicalIncludePaths() throws IOException, XmlPullParserException { if (mIncludes == null) { maybeParseBackupSchemeLocked(); } Loading @@ -407,10 +370,9 @@ public class FullBackup { } /** * @return A set of (canonical paths; requiredFlags=0) that are to be excluded from the * backup/restore set. * @return A set of canonical paths that are to be excluded from the backup/restore set. */ public synchronized ArraySet<PathWithRequiredFlags> maybeParseAndGetCanonicalExcludePaths() public synchronized ArraySet<String> maybeParseAndGetCanonicalExcludePaths() throws IOException, XmlPullParserException { if (mExcludes == null) { maybeParseBackupSchemeLocked(); Loading @@ -420,8 +382,8 @@ public class FullBackup { private void maybeParseBackupSchemeLocked() throws IOException, XmlPullParserException { // This not being null is how we know that we've tried to parse the xml already. mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>(); mExcludes = new ArraySet<PathWithRequiredFlags>(); mIncludes = new ArrayMap<String, Set<String>>(); mExcludes = new ArraySet<String>(); if (mFullBackupContent == 0) { // android:fullBackupContent="true" which means that we'll do everything. Loading Loading @@ -453,8 +415,8 @@ public class FullBackup { @VisibleForTesting public void parseBackupSchemeFromXmlLocked(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, Map<String, Set<PathWithRequiredFlags>> includes) Set<String> excludes, Map<String, Set<String>> includes) throws IOException, XmlPullParserException { int event = parser.getEventType(); // START_DOCUMENT while (event != XmlPullParser.START_TAG) { Loading @@ -479,7 +441,8 @@ public class FullBackup { case XmlPullParser.START_TAG: validateInnerTagContents(parser); final String domainFromXml = parser.getAttributeValue(null, "domain"); final File domainDirectory = getDirectoryForCriteriaDomain(domainFromXml); final File domainDirectory = getDirectoryForCriteriaDomain(domainFromXml); if (domainDirectory == null) { if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...parsing \"" + parser.getName() + "\": " Loading @@ -494,23 +457,12 @@ public class FullBackup { break; } int requiredFlags = 0; // no transport flags are required by default if (TAG_INCLUDE.equals(parser.getName())) { // requiredFlags are only supported for <include /> tag, for <exclude /> // we should always leave them as the default = 0 requiredFlags = getRequiredFlagsFromString( parser.getAttributeValue(null, "requireFlags")); } // retrieve the include/exclude set we'll be adding this rule to Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain( Set<String> activeSet = parseCurrentTagForDomain( parser, excludes, includes, domainFromXml); activeSet.add(new PathWithRequiredFlags(canonicalFile.getCanonicalPath(), requiredFlags)); activeSet.add(canonicalFile.getCanonicalPath()); if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...parsed " + canonicalFile.getCanonicalPath() + " for domain \"" + domainFromXml + "\", requiredFlags + \"" + requiredFlags + "\""); + " for domain \"" + domainFromXml + "\""); } // Special case journal files (not dirs) for sqlite database. frowny-face. Loading @@ -520,16 +472,14 @@ public class FullBackup { if ("database".equals(domainFromXml) && !canonicalFile.isDirectory()) { final String canonicalJournalPath = canonicalFile.getCanonicalPath() + "-journal"; activeSet.add(new PathWithRequiredFlags(canonicalJournalPath, requiredFlags)); activeSet.add(canonicalJournalPath); if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...automatically generated " + canonicalJournalPath + ". Ignore if nonexistent."); } final String canonicalWalPath = canonicalFile.getCanonicalPath() + "-wal"; activeSet.add(new PathWithRequiredFlags(canonicalWalPath, requiredFlags)); activeSet.add(canonicalWalPath); if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...automatically generated " + canonicalWalPath + ". Ignore if nonexistent."); Loading @@ -541,8 +491,7 @@ public class FullBackup { !canonicalFile.getCanonicalPath().endsWith(".xml")) { final String canonicalXmlPath = canonicalFile.getCanonicalPath() + ".xml"; activeSet.add(new PathWithRequiredFlags(canonicalXmlPath, requiredFlags)); activeSet.add(canonicalXmlPath); if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "...automatically generated " + canonicalXmlPath + ". Ignore if nonexistent."); Loading @@ -559,12 +508,10 @@ public class FullBackup { Log.v(TAG_XML_PARSER, " ...nothing specified (This means the entirety of app" + " data minus excludes)"); } else { for (Map.Entry<String, Set<PathWithRequiredFlags>> entry : includes.entrySet()) { for (Map.Entry<String, Set<String>> entry : includes.entrySet()) { Log.v(TAG_XML_PARSER, " domain=" + entry.getKey()); for (PathWithRequiredFlags includeData : entry.getValue()) { Log.v(TAG_XML_PARSER, " path: " + includeData.getPath() + " requiredFlags: " + includeData.getRequiredFlags()); for (String includeData : entry.getValue()) { Log.v(TAG_XML_PARSER, " " + includeData); } } } Loading @@ -573,9 +520,8 @@ public class FullBackup { if (excludes.isEmpty()) { Log.v(TAG_XML_PARSER, " ...nothing to exclude."); } else { for (PathWithRequiredFlags excludeData : excludes) { Log.v(TAG_XML_PARSER, " path: " + excludeData.getPath() + " requiredFlags: " + excludeData.getRequiredFlags()); for (String excludeData : excludes) { Log.v(TAG_XML_PARSER, " " + excludeData); } } Loading @@ -585,41 +531,20 @@ public class FullBackup { } } private int getRequiredFlagsFromString(String requiredFlags) { int flags = 0; if (requiredFlags == null || requiredFlags.length() == 0) { // requiredFlags attribute was missing or empty in <include /> tag return flags; } String[] flagsStr = requiredFlags.split("\\|"); for (String f : flagsStr) { switch (f) { case FLAG_REQUIRED_CLIENT_SIDE_ENCRYPTION: flags |= BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED; break; case FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER: flags |= BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER; break; default: Log.w(TAG, "Unrecognized requiredFlag provided, value: \"" + f + "\""); } } return flags; } private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, Map<String, Set<PathWithRequiredFlags>> includes, String domain) private Set<String> parseCurrentTagForDomain(XmlPullParser parser, Set<String> excludes, Map<String, Set<String>> includes, String domain) throws XmlPullParserException { if (TAG_INCLUDE.equals(parser.getName())) { if ("include".equals(parser.getName())) { final String domainToken = getTokenForXmlDomain(domain); Set<PathWithRequiredFlags> includeSet = includes.get(domainToken); Set<String> includeSet = includes.get(domainToken); if (includeSet == null) { includeSet = new ArraySet<PathWithRequiredFlags>(); includeSet = new ArraySet<String>(); includes.put(domainToken, includeSet); } return includeSet; } else if (TAG_EXCLUDE.equals(parser.getName())) { } else if ("exclude".equals(parser.getName())) { return excludes; } else { // Unrecognised tag => hard failure. Loading Loading @@ -664,8 +589,8 @@ public class FullBackup { /** * * @param domain Directory where the specified file should exist. Not null. * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may * be null. * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may be * null. * @return The canonical path of the file specified or null if no such file exists. */ private File extractCanonicalFile(File domain, String filePathFromXml) { Loading Loading @@ -725,25 +650,13 @@ public class FullBackup { * Let's be strict about the type of xml the client can write. If we see anything untoward, * throw an XmlPullParserException. */ private void validateInnerTagContents(XmlPullParser parser) throws XmlPullParserException { if (parser == null) { return; } switch (parser.getName()) { case TAG_INCLUDE: if (parser.getAttributeCount() > 3) { throw new XmlPullParserException("At most 3 tag attributes allowed for " + "\"include\" tag (\"domain\" & \"path\"" + " & optional \"requiredFlags\")."); } break; case TAG_EXCLUDE: private void validateInnerTagContents(XmlPullParser parser) throws XmlPullParserException { if (parser.getAttributeCount() > 2) { throw new XmlPullParserException("At most 2 tag attributes allowed for " + "\"exclude\" tag (\"domain\" & \"path\"."); throw new XmlPullParserException("At most 2 tag attributes allowed for \"" + parser.getName() + "\" tag (\"domain\" & \"path\"."); } break; default: if (!"include".equals(parser.getName()) && !"exclude".equals(parser.getName())) { throw new XmlPullParserException("A valid tag is one of \"<include/>\" or" + " \"<exclude/>. You provided \"" + parser.getName() + "\""); } Loading
core/tests/coretests/src/android/app/backup/FullBackupTest.java +26 −157 File changed.Preview size limit exceeded, changes collapsed. Show changes