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

Commit 6aa41f4c authored by Christopher Tate's avatar Christopher Tate
Browse files

Add app version to the backup metadata

We now record the version number of the app (drawn from its manifest versionCode
attribute) along with its signatures.  At restore time, we compare the version
associated with the restore set with the version present on the device.  If the
restore set is from a newer version of the app than is present on device, we do
not perform the restore operation.

Also fix the pending-backup iteration in 'dumpsys backup'.
parent 3af8e938
Loading
Loading
Loading
Loading
+28 −21
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import com.android.internal.backup.LocalTransport;
import com.android.internal.backup.IBackupTransport;

import com.android.server.PackageManagerBackupAgent;
import com.android.server.PackageManagerBackupAgent.Metadata;

import java.io.EOFException;
import java.io.File;
@@ -111,9 +112,8 @@ class BackupManagerService extends IBackupManager.Stub {
    // Do we need to back up the package manager metadata on the next pass?
    private boolean mDoPackageManager;
    private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
    // Backups that we have started.  These are separate to prevent starvation
    // if an app keeps re-enqueuing itself.
    private ArrayList<BackupRequest> mBackupQueue;

    // locking around the pending-backup management
    private final Object mQueueLock = new Object();

    // The thread performing the sequence of queued backups binds to each app's agent
@@ -296,6 +296,7 @@ class BackupManagerService extends IBackupManager.Stub {
                }

                // snapshot the pending-backup set and work on that
                ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
                File oldJournal = mJournal;
                synchronized (mQueueLock) {
                    if (mPendingBackups.size() == 0) {
@@ -303,13 +304,11 @@ class BackupManagerService extends IBackupManager.Stub {
                        break;
                    }

                    if (mBackupQueue == null) {
                        mBackupQueue = new ArrayList<BackupRequest>();
                    for (BackupRequest b: mPendingBackups.values()) {
                            mBackupQueue.add(b);
                        }
                        mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>();
                        queue.add(b);
                    }
                    Log.v(TAG, "clearing pending backups");
                    mPendingBackups.clear();

                    // Start a new backup-queue journal file too
                    if (mJournalStream != null) {
@@ -328,7 +327,7 @@ class BackupManagerService extends IBackupManager.Stub {
                    // at next boot and the journaled requests fulfilled.
                }

                (new PerformBackupThread(transport, mBackupQueue, oldJournal)).start();
                (new PerformBackupThread(transport, queue, oldJournal)).start();
                break;
            }

@@ -759,6 +758,8 @@ class BackupManagerService extends IBackupManager.Stub {
    private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
        // Allow unsigned apps, but not signed on one device and unsigned on the other
        // !!! TODO: is this the right policy?
        if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs
                + " device=" + deviceSigs);
        if ((storedSigs == null || storedSigs.length == 0)
                && (deviceSigs == null || deviceSigs.length == 0)) {
            return true;
@@ -800,6 +801,7 @@ class BackupManagerService extends IBackupManager.Stub {

        @Override
        public void run() {
            if (DEBUG) Log.v(TAG, "Beginning restore process");
            /**
             * Restore sequence:
             *
@@ -847,13 +849,19 @@ class BackupManagerService extends IBackupManager.Stub {
                            PackageInfo app = isRestorable(pkg);
                            if (app != null) {
                                // Validate against the backed-up signature block, too
                                Signature[] storedSigs
                                        = pmAgent.getRestoredSignatures(app.packageName);
                                // !!! TODO: check app version here as well
                                if (signaturesMatch(storedSigs, app.signatures)) {
                                Metadata info = pmAgent.getRestoredMetadata(app.packageName);
                                if (app.versionCode >= info.versionCode) {
                                    if (DEBUG) Log.v(TAG, "Restore version " + info.versionCode
                                            + " compatible with app version " + app.versionCode);
                                    if (signaturesMatch(info.signatures, app.signatures)) {
                                        appsToRestore.add(app);
                                    } else {
                                    Log.w(TAG, "Sig mismatch on restore of " + app.packageName);
                                        Log.w(TAG, "Sig mismatch restoring " + app.packageName);
                                    }
                                } else {
                                    Log.i(TAG, "Restore set for " + app.packageName
                                            + " is too new [" + info.versionCode
                                            + "] for installed app version " + app.versionCode);
                                }
                            }
                        }
@@ -1202,11 +1210,10 @@ class BackupManagerService extends IBackupManager.Stub {
                    pw.println(app.toString());
                }
            }
            pw.println("Pending:");
            Iterator<BackupRequest> br = mPendingBackups.values().iterator();
            while (br.hasNext()) {
            pw.println("Pending: " + mPendingBackups.size());
            for (BackupRequest req : mPendingBackups.values()) {
                pw.print("   ");
                pw.println(br);
                pw.println(req);
            }
        }
    }
+47 −22
Original line number Diff line number Diff line
@@ -40,9 +40,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

// !!!TODO: take this out
import java.util.zip.CRC32;

/**
 * We back up the signatures of each package so that during a system restore,
 * we can verify that the app whose data we think we have matches the app
@@ -57,7 +54,17 @@ public class PackageManagerBackupAgent extends BackupAgent {

    private List<ApplicationInfo> mAllApps;
    private PackageManager mPackageManager;
    private HashMap<String, Signature[]> mRestoredSignatures;
    private HashMap<String, Metadata> mRestoredSignatures;

    public class Metadata {
        public int versionCode;
        public Signature[] signatures;

        Metadata(int version, Signature[] sigs) {
            versionCode = version;
            signatures = sigs;
        }
    }

    // We're constructed with the set of applications that are participating
    // in backup.  This set changes as apps are installed & removed.
@@ -67,7 +74,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
        mRestoredSignatures = null;
    }

    public Signature[] getRestoredSignatures(String packageName) {
    public Metadata getRestoredMetadata(String packageName) {
        if (mRestoredSignatures == null) {
            return null;
        }
@@ -83,6 +90,9 @@ public class PackageManagerBackupAgent extends BackupAgent {

        // For each app we have on device, see if we've backed it up yet.  If not,
        // write its signature block to the output, keyed on the package name.
        if (DEBUG) Log.v(TAG, "onBackup()");
        ByteArrayOutputStream bufStream = new ByteArrayOutputStream();  // we'll reuse these
        DataOutputStream outWriter = new DataOutputStream(bufStream);
        for (ApplicationInfo app : mAllApps) {
            String packName = app.packageName;
            if (!existing.contains(packName)) {
@@ -90,16 +100,27 @@ public class PackageManagerBackupAgent extends BackupAgent {
                try {
                    PackageInfo info = mPackageManager.getPackageInfo(packName,
                            PackageManager.GET_SIGNATURES);
                    // build a byte array out of the signature list
                    /*
                     * Metadata for each package:
                     *
                     * int version       -- [4] the package's versionCode
                     * byte[] signatures -- [len] flattened Signature[] of the package
                     */

                    // marshall the version code in a canonical form
                    bufStream.reset();
                    outWriter.writeInt(info.versionCode);
                    byte[] versionBuf = bufStream.toByteArray();

                    byte[] sigs = flattenSignatureArray(info.signatures);

                    // !!! TODO: take out this debugging
                    if (DEBUG) {
                        CRC32 crc = new CRC32();
                        crc.update(sigs);
                        Log.i(TAG, "+ flat sig array for " + packName + " : "
                                + crc.getValue());
                        Log.v(TAG, "+ metadata for " + packName + " version=" + info.versionCode);
                    }
                    data.writeEntityHeader(packName, sigs.length);
                    // Now we can write the backup entity for this package
                    data.writeEntityHeader(packName, versionBuf.length + sigs.length);
                    data.writeEntityData(versionBuf, versionBuf.length);
                    data.writeEntityData(sigs, sigs.length);
                } catch (NameNotFoundException e) {
                    // Weird; we just found it, and now are told it doesn't exist.
@@ -113,6 +134,8 @@ public class PackageManagerBackupAgent extends BackupAgent {
            } else {
                // We've already backed up this app.  Remove it from the set so
                // we can tell at the end what has disappeared from the device.
                // !!! TODO: take out the debugging message
                if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName);
                if (!existing.remove(packName)) {
                    Log.d(TAG, "*** failed to remove " + packName + " from package set!");
                }
@@ -123,6 +146,8 @@ public class PackageManagerBackupAgent extends BackupAgent {
        // mentioned in the saved state file, but appear to no longer be present
        // on the device.  Write a deletion entity for them.
        for (String app : existing) {
            // !!! TODO: take out this msg
            if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
            try {
                data.writeEntityHeader(app, -1);
            } catch (IOException e) {
@@ -141,27 +166,29 @@ public class PackageManagerBackupAgent extends BackupAgent {
    public void onRestore(BackupDataInput data, ParcelFileDescriptor newState)
            throws IOException {
        List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
        HashMap<String, Signature[]> sigMap = new HashMap<String, Signature[]>();
        HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();

        while (data.readNextHeader()) {
            int dataSize = data.getDataSize();
            byte[] buf = new byte[dataSize];
            data.readEntityData(buf, 0, dataSize);

            Signature[] sigs = unflattenSignatureArray(buf);
            ByteArrayInputStream bufStream = new ByteArrayInputStream(buf);
            DataInputStream in = new DataInputStream(bufStream);
            int versionCode = in.readInt();

            Signature[] sigs = unflattenSignatureArray(in);
            String pkg = data.getKey();
//          !!! TODO: take out this debugging
            if (DEBUG) {
                CRC32 crc = new CRC32();
                crc.update(buf);
                Log.i(TAG, "- unflat sig array for " + pkg + " : "
                        + crc.getValue());
                Log.i(TAG, "+ restored metadata for " + pkg
                        + " versionCode=" + versionCode + " sigs=" + sigs);
            }

            ApplicationInfo app = new ApplicationInfo();
            app.packageName = pkg;
            restoredApps.add(app);
            sigMap.put(pkg, sigs);
            sigMap.put(pkg, new Metadata(versionCode, sigs));
        }

        mRestoredSignatures = sigMap;
@@ -193,9 +220,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
        return outBuf.toByteArray();
    }

    private Signature[] unflattenSignatureArray(byte[] buffer) {
        ByteArrayInputStream inBufStream = new ByteArrayInputStream(buffer);
        DataInputStream in = new DataInputStream(inBufStream);
    private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
        Signature[] sigs = null;

        try {