Parse include / exclude config specified by dataExtractionRules
1. Parse the new config and prefer it over the old config specified by android:fullBackupContent. 2. Add a new parameter to LocalTransportParameters to allow testing this functionality in CTS. Bug: 180522596 Test: atest FullBackupRulesHostSideTest Change-Id: If18edfcdb2b9637d380251f48fdbc56cca6af3ec
This commit is contained in:
@@ -19,8 +19,10 @@ package android.app.backup;
|
||||
import static android.app.backup.BackupManager.OperationType;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StringDef;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@@ -33,6 +35,7 @@ import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
@@ -93,6 +96,15 @@ public class FullBackup {
|
||||
public static final String FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION =
|
||||
"fakeClientSideEncryption";
|
||||
|
||||
@StringDef({
|
||||
ConfigSection.CLOUD_BACKUP,
|
||||
ConfigSection.DEVICE_TRANSFER
|
||||
})
|
||||
private @interface ConfigSection {
|
||||
String CLOUD_BACKUP = "cloud-backup";
|
||||
String DEVICE_TRANSFER = "device-transfer";
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify {@link BackupScheme} object by package and operation type
|
||||
* (see {@link OperationType}) it corresponds to.
|
||||
@@ -273,6 +285,7 @@ public class FullBackup {
|
||||
private final static String TAG_INCLUDE = "include";
|
||||
private final static String TAG_EXCLUDE = "exclude";
|
||||
|
||||
final int mDataExtractionRules;
|
||||
final int mFullBackupContent;
|
||||
@OperationType final int mOperationType;
|
||||
final PackageManager mPackageManager;
|
||||
@@ -394,7 +407,10 @@ public class FullBackup {
|
||||
ArraySet<PathWithRequiredFlags> mExcludes;
|
||||
|
||||
BackupScheme(Context context, @OperationType int operationType) {
|
||||
mFullBackupContent = context.getApplicationInfo().fullBackupContent;
|
||||
ApplicationInfo applicationInfo = context.getApplicationInfo();
|
||||
|
||||
mDataExtractionRules = applicationInfo.dataExtractionRulesRes;
|
||||
mFullBackupContent = applicationInfo.fullBackupContent;
|
||||
mOperationType = operationType;
|
||||
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
|
||||
mPackageManager = context.getPackageManager();
|
||||
@@ -468,34 +484,98 @@ public class FullBackup {
|
||||
mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>();
|
||||
mExcludes = new ArraySet<PathWithRequiredFlags>();
|
||||
|
||||
if (mFullBackupContent == 0) {
|
||||
// android:fullBackupContent="true" which means that we'll do everything.
|
||||
if (mFullBackupContent == 0 && mDataExtractionRules == 0) {
|
||||
// No scheme specified via either new or legacy config, will copy everything.
|
||||
if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
|
||||
Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"true\"");
|
||||
}
|
||||
} else {
|
||||
// android:fullBackupContent="@xml/some_resource".
|
||||
// Scheme is present.
|
||||
if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
|
||||
Log.v(FullBackup.TAG_XML_PARSER,
|
||||
"android:fullBackupContent - found xml resource");
|
||||
Log.v(FullBackup.TAG_XML_PARSER, "Found xml scheme: "
|
||||
+ "android:fullBackupContent=" + mFullBackupContent
|
||||
+ "; android:dataExtractionRules=" + mDataExtractionRules);
|
||||
}
|
||||
XmlResourceParser parser = null;
|
||||
|
||||
try {
|
||||
parser = mPackageManager
|
||||
.getResourcesForApplication(mPackageName)
|
||||
.getXml(mFullBackupContent);
|
||||
parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes);
|
||||
parseSchemeForOperationType(mOperationType);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Throw it as an IOException
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
if (parser != null) {
|
||||
parser.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSchemeForOperationType(@OperationType int operationType)
|
||||
throws PackageManager.NameNotFoundException, IOException, XmlPullParserException {
|
||||
String configSection = getConfigSectionForOperationType(operationType);
|
||||
if (configSection == null) {
|
||||
Slog.w(TAG, "Given operation type isn't supported by backup scheme: "
|
||||
+ operationType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDataExtractionRules != 0) {
|
||||
// New config is present. Use it if it has configuration for this operation
|
||||
// type.
|
||||
try (XmlResourceParser parser = getParserForResource(mDataExtractionRules)) {
|
||||
parseNewBackupSchemeFromXmlLocked(parser, configSection, mExcludes, mIncludes);
|
||||
}
|
||||
if (!mExcludes.isEmpty() || !mIncludes.isEmpty()) {
|
||||
// Found configuration in the new config, we will use it.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/180523564): Ignore the old config for apps targeting Android S+ during D2D.
|
||||
|
||||
if (mFullBackupContent != 0) {
|
||||
// Fall back to the old config.
|
||||
try (XmlResourceParser parser = getParserForResource(mFullBackupContent)) {
|
||||
parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getConfigSectionForOperationType(@OperationType int operationType) {
|
||||
switch (operationType) {
|
||||
case OperationType.BACKUP:
|
||||
return ConfigSection.CLOUD_BACKUP;
|
||||
case OperationType.MIGRATION:
|
||||
return ConfigSection.DEVICE_TRANSFER;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private XmlResourceParser getParserForResource(int resourceId)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
return mPackageManager
|
||||
.getResourcesForApplication(mPackageName)
|
||||
.getXml(resourceId);
|
||||
}
|
||||
|
||||
private void parseNewBackupSchemeFromXmlLocked(XmlPullParser parser,
|
||||
@ConfigSection String configSection,
|
||||
Set<PathWithRequiredFlags> excludes,
|
||||
Map<String, Set<PathWithRequiredFlags>> includes)
|
||||
throws IOException, XmlPullParserException {
|
||||
verifyTopLevelTag(parser, "data-extraction-rules");
|
||||
|
||||
int event;
|
||||
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||
if (event != XmlPullParser.START_TAG || !configSection.equals(parser.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO(b/180523028): Parse required attributes for rules (e.g. encryption).
|
||||
parseRules(parser, excludes, includes, Optional.of(0), configSection);
|
||||
}
|
||||
|
||||
logParsingResults(excludes, includes);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void parseBackupSchemeFromXmlLocked(XmlPullParser parser,
|
||||
Set<PathWithRequiredFlags> excludes,
|
||||
@@ -503,7 +583,7 @@ public class FullBackup {
|
||||
throws IOException, XmlPullParserException {
|
||||
verifyTopLevelTag(parser, "full-backup-content");
|
||||
|
||||
parseRules(parser, excludes, includes, Optional.empty());
|
||||
parseRules(parser, excludes, includes, Optional.empty(), "full-backup-content");
|
||||
|
||||
logParsingResults(excludes, includes);
|
||||
}
|
||||
@@ -532,10 +612,12 @@ public class FullBackup {
|
||||
private void parseRules(XmlPullParser parser,
|
||||
Set<PathWithRequiredFlags> excludes,
|
||||
Map<String, Set<PathWithRequiredFlags>> includes,
|
||||
Optional<Integer> maybeRequiredFlags)
|
||||
Optional<Integer> maybeRequiredFlags,
|
||||
String endingTag)
|
||||
throws IOException, XmlPullParserException {
|
||||
int event;
|
||||
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& !parser.getName().equals(endingTag)) {
|
||||
switch (event) {
|
||||
case XmlPullParser.START_TAG:
|
||||
validateInnerTagContents(parser);
|
||||
|
||||
@@ -27,9 +27,11 @@ public class LocalTransportParameters extends KeyValueSettingObserver {
|
||||
private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
|
||||
private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
|
||||
private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
|
||||
private static final String KEY_IS_DEVICE_TRANSFER = "is_device_transfer";
|
||||
|
||||
private boolean mFakeEncryptionFlag;
|
||||
private boolean mIsNonIncrementalOnly;
|
||||
private boolean mIsDeviceTransfer;
|
||||
|
||||
public LocalTransportParameters(Handler handler, ContentResolver resolver) {
|
||||
super(handler, resolver, Settings.Secure.getUriFor(SETTING));
|
||||
@@ -43,6 +45,10 @@ public class LocalTransportParameters extends KeyValueSettingObserver {
|
||||
return mIsNonIncrementalOnly;
|
||||
}
|
||||
|
||||
boolean isDeviceTransfer() {
|
||||
return mIsDeviceTransfer;
|
||||
}
|
||||
|
||||
public String getSettingValue(ContentResolver resolver) {
|
||||
return Settings.Secure.getString(resolver, SETTING);
|
||||
}
|
||||
@@ -50,5 +56,6 @@ public class LocalTransportParameters extends KeyValueSettingObserver {
|
||||
public void update(KeyValueListParser parser) {
|
||||
mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false);
|
||||
mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false);
|
||||
mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user