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

Commit f7cbb1fc authored by Christopher Tate's avatar Christopher Tate
Browse files

Always check restore against the latest backend metadata

Bug 16484934

Change-Id: I472a7db89a94b9804f6ea94c25da206dd111a497
parent fcd8b20e
Loading
Loading
Loading
Loading
+70 −91
Original line number Diff line number Diff line
@@ -6558,52 +6558,35 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
        private  void startRestore() {
            sendStartRestore(mAcceptSet.size());

            UnifiedRestoreState nextState = UnifiedRestoreState.RESTORE_FINISHED;
            try {
                // If we don't yet have PM metadata for this token, synthesize an
                // entry for the PM pseudopackage and make it the first to be
                // restored.
                String transportDir = mTransport.transportDirName();
                mStateDir = new File(mBaseStateDir, transportDir);
                File metadataDir = new File(mStateDir, "_metadata");
                metadataDir.mkdirs();
                File metadataFile = new File(metadataDir, Long.toHexString(mToken));
                try {
                    // PM info is cached in $BASE/transport/_metadata/$TOKEN
                    mPmAgent = new PackageManagerBackupAgent(metadataFile);
                } catch (IOException e) {
                    // Nope, we need to get it via restore
                    if (MORE_DEBUG) Slog.v(TAG, "Need to restore @pm@");

                // Fetch the current metadata from the dataset first
                PackageInfo pmPackage = new PackageInfo();
                pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                mAcceptSet.add(0, pmPackage);
                }

                PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
                mStatus = mTransport.startRestore(mToken, packages);
                if (mStatus != BackupTransport.TRANSPORT_OK) {
                    Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
                    mStatus = BackupTransport.TRANSPORT_ERROR;
                    nextState = UnifiedRestoreState.FINAL;
                    executeNextState(UnifiedRestoreState.FINAL);
                    return;
                }

                if (mPmAgent == null) {
                    if (DEBUG) {
                        Slog.v(TAG, "Need to fetch metadata for token "
                                + Long.toHexString(mToken));
                    }
                RestoreDescription desc = mTransport.nextRestorePackage();
                if (desc == null) {
                    Slog.e(TAG, "No restore metadata available; halting");
                    mStatus = BackupTransport.TRANSPORT_ERROR;
                        nextState = UnifiedRestoreState.FINAL;
                    executeNextState(UnifiedRestoreState.FINAL);
                    return;
                }
                if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
                    Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
                    mStatus = BackupTransport.TRANSPORT_ERROR;
                        nextState = UnifiedRestoreState.FINAL;
                    executeNextState(UnifiedRestoreState.FINAL);
                    return;
                }

@@ -6617,58 +6600,35 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                }
                initiateOneRestore(mCurrentPackage, 0);
                // The PM agent called operationComplete() already, because our invocation
                    // of it is process-local and therefore synchronous.  That means that a
                    // RUNNING_QUEUE message is already enqueued.  Only if we're unable to
                    // proceed with running the queue do we remove that pending message and
                    // jump straight to the FINAL state.
                // of it is process-local and therefore synchronous.  That means that the
                // next-state message (RUNNING_QUEUE) is already enqueued.  Only if we're
                // unable to proceed with running the queue do we remove that pending
                // message and jump straight to the FINAL state.

                // Verify that the backup set includes metadata.  If not, we can't do
                // signature/version verification etc, so we simply do not proceed with
                // the restore operation.
                if (!mPmAgent.hasMetadata()) {
                        Slog.e(TAG, "No restore metadata available, so not restoring settings");
                    Slog.e(TAG, "No restore metadata available, so not restoring");
                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                            PACKAGE_MANAGER_SENTINEL,
                            "Package manager restore metadata missing");
                    mStatus = BackupTransport.TRANSPORT_ERROR;
                    mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
                        nextState = UnifiedRestoreState.FINAL;
                    executeNextState(UnifiedRestoreState.FINAL);
                    return;
                }

                // Success; cache the metadata and continue as expected with the
                    // RUNNING_QUEUE step already enqueued.
                    if (DEBUG) {
                        Slog.v(TAG, "Got metadata; caching and proceeding to restore");
                    }
                    try {
                        mPmAgent.saveToDisk(metadataFile);
                    } catch (IOException e) {
                        // Something bad; we need to abort
                        Slog.e(TAG, "Unable to write restored metadata");
                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                                PACKAGE_MANAGER_SENTINEL,
                                "Unable to write restored metadata");
                        mStatus = BackupTransport.TRANSPORT_ERROR;
                        mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
                        nextState = UnifiedRestoreState.FINAL;
                        return;
                    }
                } else {
                    // We have the PMBA already, so we can proceed directly to
                    // the RUNNING_QUEUE state ourselves.
                    if (MORE_DEBUG) Slog.v(TAG, "PMBA from cache; proceeding to run queue");
                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
                }
                // next state already enqueued

            } catch (RemoteException e) {
                // If we lost the transport at any time, halt
                Slog.e(TAG, "Unable to contact transport for restore");
                mStatus = BackupTransport.TRANSPORT_ERROR;
                mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
                nextState = UnifiedRestoreState.FINAL;
                executeNextState(UnifiedRestoreState.FINAL);
                return;
            } finally {
                executeNextState(nextState);
            }
        }

@@ -7267,6 +7227,13 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF

            final UnifiedRestoreState nextState;
            switch (mState) {
                case INITIAL:
                    // We've just (manually) restored the PMBA.  It doesn't need the
                    // additional restore-finished callback so we bypass that and go
                    // directly to running the queue.
                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
                    break;

                case RESTORE_KEYVALUE:
                case RESTORE_FULL: {
                    // Okay, we've just heard back from the agent that it's done with
@@ -8187,6 +8154,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                pkg.packageName = packageName;

                mWakelock.acquire();
                if (MORE_DEBUG) {
                    Slog.d(TAG, "Restore at install of " + packageName);
                }
                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                msg.obj = new RestoreParams(transport, dirName, null,
                        restoreSet, pkg, token);
@@ -8368,6 +8338,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                    if (token == mRestoreSets[i].token) {
                        long oldId = Binder.clearCallingIdentity();
                        mWakelock.acquire();
                        if (MORE_DEBUG) {
                            Slog.d(TAG, "restoreAll() kicking off");
                        }
                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
                                observer, token);
@@ -8439,6 +8412,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                    if (token == mRestoreSets[i].token) {
                        long oldId = Binder.clearCallingIdentity();
                        mWakelock.acquire();
                        if (MORE_DEBUG) {
                            Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
                        }
                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
                                packages, packages.length > 1);
@@ -8518,6 +8494,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
            // Ready to go:  enqueue the restore request and claim success
            long oldId = Binder.clearCallingIdentity();
            mWakelock.acquire();
            if (MORE_DEBUG) {
                Slog.d(TAG, "restorePackage() : " + packageName);
            }
            Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
            msg.obj = new RestoreParams(mRestoreTransport, dirName,
                    observer, token, app, 0);
+0 −91
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -48,9 +47,6 @@ import java.util.List;
import java.util.Set;

import java.util.Objects;
import java.util.Map.Entry;

import libcore.io.IoUtils;

/**
 * We back up the signatures of each package so that during a system restore,
@@ -132,93 +128,6 @@ public class PackageManagerBackupAgent extends BackupAgent {
        mStoredIncrementalVersion = Build.VERSION.INCREMENTAL;
    }

    /**
     * Reconstitute a PMBA from its on-disk format.  This is used for persistence
     * of the ancestral dataset's metadata.  See {@link #saveToDisk()} for
     * details of the file format.
     */
    PackageManagerBackupAgent(File cache) throws IOException {
        FileInputStream fin = new FileInputStream(cache);
        BufferedInputStream bin = new BufferedInputStream(fin, 32 * 1024);
        DataInputStream in = new DataInputStream(bin);

        int version = in.readInt();
        // We can currently only handle the initial version format
        if (version == ANCESTRAL_RECORD_VERSION) {
            mStoredSdkVersion = in.readInt();
            mStoredIncrementalVersion = in.readUTF();

            int nPackages = in.readInt();
            if (nPackages > 0) {
                HashMap<String, Metadata> restoredMetadata =
                        new HashMap<String, Metadata>(nPackages);
                ArrayList<byte[]> hashes = null;
                for (int pack = 0; pack < nPackages; pack++) {
                    final String name = in.readUTF();
                    final int versionCode = in.readInt();
                    final int nHashes = in.readInt();
                    if (nHashes > 0) {
                        hashes = new ArrayList<byte[]>(nHashes);
                        for (int i = 0; i < nHashes; i++) {
                            int len = in.readInt();
                            byte[] hash = new byte[len];
                            in.read(hash);
                            hashes.add(hash);
                        }
                    }
                    restoredMetadata.put(name, new Metadata(versionCode, hashes));
                }
                mRestoredSignatures = restoredMetadata;
            }
        }
    }

    public void saveToDisk(File cache) throws IOException {
        // On disk format is very similar to the key/value format:
        //
        // Int: disk format version, currently 1
        // Int: VERSION.SDK_INT of source device
        // UTF: VERSION.INCREMENTAL string, for reference
        //
        // Int: number of packages represented in this file
        //
        // Per package if number > 0:
        //     UTF: package name
        //     Int: versionCode of the package
        //     Int: number of signature hash blocks for this package
        //     Per signature hash block:
        //         Int: size of block
        //         byte[]: block itself if size of block > 0
        FileOutputStream of = new FileOutputStream(cache);
        BufferedOutputStream bout = new BufferedOutputStream(of, 32*1024);
        DataOutputStream out = new DataOutputStream(bout);

        out.writeInt(ANCESTRAL_RECORD_VERSION);
        out.writeInt(mStoredSdkVersion);
        out.writeUTF(mStoredIncrementalVersion);

        out.writeInt(mRestoredSignatures.size());
        if (mRestoredSignatures.size() > 0) {
            Set<Entry<String, Metadata>> entries = mRestoredSignatures.entrySet();
            for (Entry<String, Metadata> i : entries) {
                final Metadata m = i.getValue();
                final int nHashes = (m.sigHashes != null) ? m.sigHashes.size() : 0;
                out.writeUTF(i.getKey());
                out.writeInt(m.versionCode);
                out.writeInt(nHashes);
                for (int h = 0; h < nHashes; h++) {
                    byte[] hash = m.sigHashes.get(h);
                    out.writeInt(hash.length);
                    if (hash.length > 0) {
                        out.write(hash);
                    }
                }
            }
        }
        out.flush();
        IoUtils.closeQuietly(out);
    }

    // We will need to refresh our understanding of what is eligible for
    // backup periodically; this entry point serves that purpose.
    public void evaluateStorablePackages() {