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

Commit f4048242 authored by Joël Stemmer's avatar Joël Stemmer Committed by Android (Google) Code Review
Browse files

Merge "Stop backing up the ancestral record version unless it has changed" into main

parents 6b672943 8d8a538c
Loading
Loading
Loading
Loading
+85 −24
Original line number Original line Diff line number Diff line
@@ -99,6 +99,9 @@ public class PackageManagerBackupAgent extends BackupAgent {
    // The version info of each backed-up app as read from the state file
    // The version info of each backed-up app as read from the state file
    private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>();
    private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>();


    // The ancestral record version as read from the state file
    private int mStoredAncestralRecordVersion;

    private final HashSet<String> mExisting = new HashSet<String>();
    private final HashSet<String> mExisting = new HashSet<String>();
    private int mStoredSdkVersion;
    private int mStoredSdkVersion;
    private String mStoredIncrementalVersion;
    private String mStoredIncrementalVersion;
@@ -233,17 +236,32 @@ public class PackageManagerBackupAgent extends BackupAgent {
         * int ancestralRecordVersion -- the version of the format in which this backup set is
         * int ancestralRecordVersion -- the version of the format in which this backup set is
         *                               produced
         *                               produced
         */
         */
        boolean upgradingAncestralRecordVersion = false;
        try {
        try {
            if (DEBUG) Slog.v(TAG, "Storing ancestral record version key");
            if (!mExisting.contains(ANCESTRAL_RECORD_KEY)) {
                // The old state does not store info on ancestral record
                Slog.v(
                        TAG,
                        "No ancestral record version in the old state. Storing "
                                + "ancestral record version key");
                outputBufferStream.writeInt(ANCESTRAL_RECORD_VERSION);
                outputBufferStream.writeInt(ANCESTRAL_RECORD_VERSION);
                writeEntity(data, ANCESTRAL_RECORD_KEY, outputBuffer.toByteArray());
                writeEntity(data, ANCESTRAL_RECORD_KEY, outputBuffer.toByteArray());
        } catch (IOException e) {
                upgradingAncestralRecordVersion = true;
            // Real error writing data
            } else if (mStoredAncestralRecordVersion != ANCESTRAL_RECORD_VERSION) {
            Slog.e(TAG, "Unable to write package backup data file!");
                // The current ancestral record version has changed from the old state
            return;
                Slog.v(
                        TAG,
                        "Ancestral record version has changed from old state. Storing"
                                + "ancestral record version key");
                outputBufferStream.writeInt(ANCESTRAL_RECORD_VERSION);
                writeEntity(data, ANCESTRAL_RECORD_KEY, outputBuffer.toByteArray());
                upgradingAncestralRecordVersion = true;
                mExisting.remove(ANCESTRAL_RECORD_KEY);
            } else {
                if (DEBUG) Slog.v(TAG, "Ancestral record version has not changed");
                mExisting.remove(ANCESTRAL_RECORD_KEY);
            }
            }


        try {
            /*
            /*
             * Global metadata:
             * Global metadata:
             *
             *
@@ -286,13 +304,16 @@ public class PackageManagerBackupAgent extends BackupAgent {
                    }
                    }


                    if (mExisting.contains(packName)) {
                    if (mExisting.contains(packName)) {
                        // We have backed up this app before.  Check whether the version
                        // We have backed up this app before.  If the current ancestral record
                        // of the backup matches the version of the current app; if they
                        // version is the same as what is in the old state, check whether the
                        // version of the backup matches the version of the current app; if they
                        // don't match, the app has been updated and we need to store its
                        // don't match, the app has been updated and we need to store its
                        // metadata again.  In either case, take it out of mExisting so that
                        // metadata again.  In either case, take it out of mExisting so that
                        // we don't consider it deleted later.
                        // we don't consider it deleted later.
                        mExisting.remove(packName);
                        mExisting.remove(packName);
                        if (info.getLongVersionCode() == mStateVersions.get(packName).versionCode) {
                        if (!upgradingAncestralRecordVersion
                                && info.getLongVersionCode()
                                        == mStateVersions.get(packName).versionCode) {
                            continue;
                            continue;
                        }
                        }
                    }
                    }
@@ -346,11 +367,27 @@ public class PackageManagerBackupAgent extends BackupAgent {


            // At this point, the only entries in 'existing' are apps that were
            // At this point, the only entries in 'existing' are apps that were
            // mentioned in the saved state file, but appear to no longer be present
            // mentioned in the saved state file, but appear to no longer be present
            // on the device.  We want to preserve the entry for them, however,
            // on the device.
            // because we want the right thing to happen if the user goes through
            if (!mExisting.isEmpty()) {
            // a backup / uninstall / backup / reinstall sequence.
                // If the ancestral record version has changed from the previous state we delete the
                // existing keys for apps that are no longer installed. We should do this, otherwise
                // we'd leave a key/value pair behind in the old format which could cause problems.
                if (upgradingAncestralRecordVersion) {
                    for (String pkgName : mExisting) {
                        Slog.i(
                                TAG,
                                "Ancestral state updated - Deleting uninstalled package: "
                                        + pkgName
                                        + " from existing backup");
                        data.writeEntityHeader(pkgName, -1);
                    }
                    mExisting.clear();
                } else {
                    // If the ancestral record version is unchanged from the previous state, we
                    // don't to anything to preserve the key/value entry for them. We do this
                    // because we want the right thing to happen if the user goes through a
                    // backup / uninstall / backup / reinstall sequence.
                    if (DEBUG) {
                    if (DEBUG) {
                if (mExisting.size() > 0) {
                        StringBuilder sb = new StringBuilder(64);
                        StringBuilder sb = new StringBuilder(64);
                        sb.append("Preserving metadata for deleted packages:");
                        sb.append("Preserving metadata for deleted packages:");
                        for (String app : mExisting) {
                        for (String app : mExisting) {
@@ -360,6 +397,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
                        Slog.v(TAG, sb.toString());
                        Slog.v(TAG, sb.toString());
                    }
                    }
                }
                }
            }
        } catch (IOException e) {
        } catch (IOException e) {
            // Real error writing data
            // Real error writing data
            Slog.e(TAG, "Unable to write package backup data file!");
            Slog.e(TAG, "Unable to write package backup data file!");
@@ -506,6 +544,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
        mStoredHomeComponent = null;
        mStoredHomeComponent = null;
        mStoredHomeVersion = 0;
        mStoredHomeVersion = 0;
        mStoredHomeSigHashes = null;
        mStoredHomeSigHashes = null;
        mStoredAncestralRecordVersion = UNDEFINED_ANCESTRAL_RECORD_VERSION;


        // The state file is just the list of app names we have stored signatures for
        // The state file is just the list of app names we have stored signatures for
        // with the exception of the metadata block, to which is also appended the
        // with the exception of the metadata block, to which is also appended the
@@ -541,7 +580,25 @@ public class PackageManagerBackupAgent extends BackupAgent {
                ignoreExisting = true;
                ignoreExisting = true;
            }
            }


            // First comes the preferred home app data, if any, headed by the DEFAULT_HOME_KEY tag
            // First comes the ancestral record block headed by the ANCESTRAL_RECORD_KEY tag
            if (pkg.equals(ANCESTRAL_RECORD_KEY)) {
                mStoredAncestralRecordVersion = in.readInt();
                if (!ignoreExisting) {
                    mExisting.add(ANCESTRAL_RECORD_KEY);
                }
                pkg = in.readUTF(); // set up for the next block of state
            } else {
                // This is an old version of the state file in which ANCESTRAL_RECORD_KEY is not
                // stored. In this case onBackup will write the ANCESTRAL_KEY_VALUE to the new
                // state.
                Slog.i(
                        TAG,
                        "Older version of saved state - does not contain ancestral record "
                                + "version");
            }

            // Then comes the preferred home app data, if any, headed by the DEFAULT_HOME_KEY tag
            // Note that Default home app data is no longer backed up by this agent.
            if (pkg.equals(DEFAULT_HOME_KEY)) {
            if (pkg.equals(DEFAULT_HOME_KEY)) {
                // flattened component name, version, signature of the home app
                // flattened component name, version, signature of the home app
                mStoredHomeComponent = ComponentName.unflattenFromString(in.readUTF());
                mStoredHomeComponent = ComponentName.unflattenFromString(in.readUTF());
@@ -606,6 +663,10 @@ public class PackageManagerBackupAgent extends BackupAgent {
            out.writeUTF(STATE_FILE_HEADER);
            out.writeUTF(STATE_FILE_HEADER);
            out.writeInt(STATE_FILE_VERSION);
            out.writeInt(STATE_FILE_VERSION);


            // Record the ancestral record
            out.writeUTF(ANCESTRAL_RECORD_KEY);
            out.writeInt(ANCESTRAL_RECORD_VERSION);

            // Conclude with the metadata block
            // Conclude with the metadata block
            out.writeUTF(GLOBAL_METADATA_KEY);
            out.writeUTF(GLOBAL_METADATA_KEY);
            out.writeInt(Build.VERSION.SDK_INT);
            out.writeInt(Build.VERSION.SDK_INT);
+51 −12
Original line number Original line Diff line number Diff line
@@ -55,20 +55,21 @@ public class PackageManagerBackupAgentTest {


    @Rule public TemporaryFolder folder = new TemporaryFolder();
    @Rule public TemporaryFolder folder = new TemporaryFolder();


    private PackageManager mPackageManager;
    private PackageManagerBackupAgent mPackageManagerBackupAgent;
    private PackageManagerBackupAgent mPackageManagerBackupAgent;
    private ImmutableList<PackageInfo> mPackages;
    private ImmutableList<PackageInfo> mPackages;
    private File mBackupData, mOldState, mNewState;
    private File mBackupData, mOldState, mNewState;


    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        PackageManager packageManager = getApplicationContext().getPackageManager();
        mPackageManager = getApplicationContext().getPackageManager();


        PackageInfo existingPackageInfo =
        PackageInfo existingPackageInfo =
                packageManager.getPackageInfoAsUser(
                mPackageManager.getPackageInfoAsUser(
                        EXISTING_PACKAGE_NAME, PackageManager.GET_SIGNING_CERTIFICATES, USER_ID);
                        EXISTING_PACKAGE_NAME, PackageManager.GET_SIGNING_CERTIFICATES, USER_ID);
        mPackages = ImmutableList.of(existingPackageInfo);
        mPackages = ImmutableList.of(existingPackageInfo);
        mPackageManagerBackupAgent =
        mPackageManagerBackupAgent =
                new PackageManagerBackupAgent(packageManager, mPackages, USER_ID);
                new PackageManagerBackupAgent(mPackageManager, mPackages, USER_ID);


        mBackupData = folder.newFile("backup_data");
        mBackupData = folder.newFile("backup_data");
        mOldState = folder.newFile("old_state");
        mOldState = folder.newFile("old_state");
@@ -101,11 +102,8 @@ public class PackageManagerBackupAgentTest {


        runBackupAgentOnBackup();
        runBackupAgentOnBackup();


        // We shouldn't have written anything, but a known issue is that we always write the
        // ancestral record version.
        ImmutableMap<String, Optional<ByteBuffer>> keyValues = getKeyValues(mBackupData);
        ImmutableMap<String, Optional<ByteBuffer>> keyValues = getKeyValues(mBackupData);
        assertThat(keyValues.keySet())
        assertThat(keyValues).isEmpty();
                .containsExactly(PackageManagerBackupAgent.ANCESTRAL_RECORD_KEY);
        assertThat(mNewState.length()).isGreaterThan(0);
        assertThat(mNewState.length()).isGreaterThan(0);
        assertThat(mNewState.length()).isEqualTo(mOldState.length());
        assertThat(mNewState.length()).isEqualTo(mOldState.length());
    }
    }
@@ -122,9 +120,7 @@ public class PackageManagerBackupAgentTest {


        // Note that uninstalledPackageName should not exist, i.e. it did not get deleted.
        // Note that uninstalledPackageName should not exist, i.e. it did not get deleted.
        ImmutableMap<String, Optional<ByteBuffer>> keyValues = getKeyValues(mBackupData);
        ImmutableMap<String, Optional<ByteBuffer>> keyValues = getKeyValues(mBackupData);
        assertThat(keyValues.keySet())
        assertThat(keyValues.keySet()).containsExactly(EXISTING_PACKAGE_NAME);
                .containsExactly(
                        PackageManagerBackupAgent.ANCESTRAL_RECORD_KEY, EXISTING_PACKAGE_NAME);
        assertThat(mNewState.length()).isGreaterThan(0);
        assertThat(mNewState.length()).isGreaterThan(0);
    }
    }


@@ -133,7 +129,9 @@ public class PackageManagerBackupAgentTest {
        String uninstalledPackageName = "does.not.exist";
        String uninstalledPackageName = "does.not.exist";
        writeLegacyStateFile(
        writeLegacyStateFile(
                mOldState,
                mOldState,
                ImmutableList.of(createPackage(uninstalledPackageName, 1), mPackages.getFirst()));
                ImmutableList.of(createPackage(uninstalledPackageName, 1), mPackages.getFirst()),
                /* writeStateFileVersion= */ false,
                /* writeAncestralRecordVersion= */ false);


        runBackupAgentOnBackup();
        runBackupAgentOnBackup();


@@ -146,6 +144,30 @@ public class PackageManagerBackupAgentTest {
        assertThat(mNewState.length()).isGreaterThan(0);
        assertThat(mNewState.length()).isGreaterThan(0);
    }
    }


    @Test
    public void onBackup_noAncestralRecordInfo_deletesUninstalledPackagesFromBackup()
            throws Exception {
        PackageInfo pkgNotInstalled = createPackage("does.not.exist", 2);
        PackageInfo pkgInstalled =
                mPackageManager.getPackageInfoAsUser(
                        EXISTING_PACKAGE_NAME, PackageManager.GET_SIGNING_CERTIFICATES, USER_ID);
        writeLegacyStateFile(
                mOldState,
                ImmutableList.of(pkgInstalled, pkgNotInstalled),
                /* writeStateFileVersion= */ true,
                /* writeAncestralRecordVersion= */ false);

        runBackupAgentOnBackup();

        ImmutableMap<String, Optional<ByteBuffer>> keyValues = getKeyValues(mBackupData);
        assertThat(keyValues.keySet())
                .containsExactly(
                        PackageManagerBackupAgent.ANCESTRAL_RECORD_KEY,
                        pkgInstalled.packageName,
                        pkgNotInstalled.packageName);
        assertThat(keyValues).containsEntry(pkgNotInstalled.packageName, Optional.empty());
    }

    @Test
    @Test
    public void onRestore_recentBackup_restoresBackup() throws Exception {
    public void onRestore_recentBackup_restoresBackup() throws Exception {
        runBackupAgentOnBackup();
        runBackupAgentOnBackup();
@@ -229,7 +251,11 @@ public class PackageManagerBackupAgentTest {
    }
    }


    /** This creates a legacy state file in which {@code STATE_FILE_HEADER} was not yet present. */
    /** This creates a legacy state file in which {@code STATE_FILE_HEADER} was not yet present. */
    private static void writeLegacyStateFile(File stateFile, ImmutableList<PackageInfo> packages)
    private static void writeLegacyStateFile(
            File stateFile,
            ImmutableList<PackageInfo> packages,
            boolean writeStateFileVersion,
            boolean writeAncestralRecordVersion)
            throws Exception {
            throws Exception {
        try (ParcelFileDescriptor stateFileDescriptor = openForWriting(stateFile);
        try (ParcelFileDescriptor stateFileDescriptor = openForWriting(stateFile);
                DataOutputStream out =
                DataOutputStream out =
@@ -237,6 +263,19 @@ public class PackageManagerBackupAgentTest {
                                new BufferedOutputStream(
                                new BufferedOutputStream(
                                        new FileOutputStream(
                                        new FileOutputStream(
                                                stateFileDescriptor.getFileDescriptor())))) {
                                                stateFileDescriptor.getFileDescriptor())))) {

            if (writeStateFileVersion) {
                // state file version header
                out.writeUTF(PackageManagerBackupAgent.STATE_FILE_HEADER);
                out.writeInt(PackageManagerBackupAgent.STATE_FILE_VERSION);
            }

            if (writeAncestralRecordVersion) {
                // Record the ancestral record
                out.writeUTF(PackageManagerBackupAgent.ANCESTRAL_RECORD_KEY);
                out.writeInt(PackageManagerBackupAgent.ANCESTRAL_RECORD_VERSION);
            }

            out.writeUTF(PackageManagerBackupAgent.GLOBAL_METADATA_KEY);
            out.writeUTF(PackageManagerBackupAgent.GLOBAL_METADATA_KEY);
            out.writeInt(Build.VERSION.SDK_INT);
            out.writeInt(Build.VERSION.SDK_INT);
            out.writeUTF(Build.VERSION.INCREMENTAL);
            out.writeUTF(Build.VERSION.INCREMENTAL);