diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index f7ed6f1f2feb4..3701ea8259337 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -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 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>(); mExcludes = new ArraySet(); - 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 excludes, + Map> 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 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 excludes, Map> includes, - Optional maybeRequiredFlags) + Optional 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); diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java index 8b4db92910f5d..2946db3cdcf0c 100644 --- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java @@ -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); } }