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

Commit 267f0a6b authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

Merge "Workaround NPE caused by packages missing signatures." into froyo

parents 779a4ca8 e684d958
Loading
Loading
Loading
Loading
+83 −89
Original line number Diff line number Diff line
@@ -122,8 +122,8 @@ public class PackageManagerBackupAgent extends BackupAgent {
            ParcelFileDescriptor newState) {
        if (DEBUG) Slog.v(TAG, "onBackup()");

        ByteArrayOutputStream bufStream = new ByteArrayOutputStream();  // we'll reuse these
        DataOutputStream outWriter = new DataOutputStream(bufStream);
        ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();  // we'll reuse these
        DataOutputStream outputBufferStream = new DataOutputStream(outputBuffer);
        parseStateFile(oldState);

        // If the stored version string differs, we need to re-backup all
@@ -148,11 +148,9 @@ public class PackageManagerBackupAgent extends BackupAgent {
             */
            if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
                if (DEBUG) Slog.v(TAG, "Storing global metadata key");
                outWriter.writeInt(Build.VERSION.SDK_INT);
                outWriter.writeUTF(Build.VERSION.INCREMENTAL);
                byte[] metadata = bufStream.toByteArray();
                data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length);
                data.writeEntityData(metadata, metadata.length);
                outputBufferStream.writeInt(Build.VERSION.SDK_INT);
                outputBufferStream.writeUTF(Build.VERSION.INCREMENTAL);
                writeEntity(data, GLOBAL_METADATA_KEY, outputBuffer.toByteArray());
            } else {
                if (DEBUG) Slog.v(TAG, "Global metadata key already stored");
                // don't consider it to have been skipped/deleted
@@ -178,23 +176,25 @@ public class PackageManagerBackupAgent extends BackupAgent {
                        continue;
                    }

                    boolean doBackup = false;
                    if (!mExisting.contains(packName)) {
                        // We haven't backed up this app before
                        doBackup = true;
                    } else {
                        // We *have* backed this one up before.  Check whether the version
                    if (mExisting.contains(packName)) {
                        // We have backed up this app before.  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
                        // metadata again.  In either case, take it out of mExisting so that
                        // we don't consider it deleted later.
                        if (info.versionCode != mStateVersions.get(packName).versionCode) {
                            doBackup = true;
                        }
                        mExisting.remove(packName);
                        if (info.versionCode == mStateVersions.get(packName).versionCode) {
                            continue;
                        }
                    }
                    
                    if (info.signatures == null || info.signatures.length == 0)
                    {
                        Slog.w(TAG, "Not backing up package " + packName
                                + " since it appears to have no signatures.");
                        continue;
                    }

                    if (doBackup) {
                    // We need to store this app's metadata
                    /*
                     * Metadata for each package:
@@ -204,23 +204,18 @@ public class PackageManagerBackupAgent extends BackupAgent {
                     */

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

                        byte[] sigs = flattenSignatureArray(info.signatures);
                    outputBuffer.reset();
                    outputBufferStream.writeInt(info.versionCode);
                    writeSignatureArray(outputBufferStream, info.signatures);

                    if (DEBUG) {
                            Slog.v(TAG, "+ metadata for " + packName
                        Slog.v(TAG, "+ writing metadata for " + packName
                                + " version=" + info.versionCode
                                    + " versionLen=" + versionBuf.length
                                    + " sigsLen=" + sigs.length);
                                + " entityLen=" + outputBuffer.size());
                    }
                    
                    // 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);
                    }
                    writeEntity(data, packName, outputBuffer.toByteArray());
                }
            }

@@ -246,6 +241,12 @@ public class PackageManagerBackupAgent extends BackupAgent {
        writeStateFile(mAllPackages, newState);
    }
    
    private static void writeEntity(BackupDataOutput data, String key, byte[] bytes)
            throws IOException {
        data.writeEntityHeader(key, bytes.length);
        data.writeEntityData(bytes, bytes.length);
    }

    // "Restore" here is a misnomer.  What we're really doing is reading back the
    // set of app signatures associated with each backed-up app in this restore
    // image.  We'll use those later to determine what we can legitimately restore.
@@ -263,13 +264,13 @@ public class PackageManagerBackupAgent extends BackupAgent {
            if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize);

            // generic setup to parse any entity data
            byte[] dataBuf = new byte[dataSize];
            data.readEntityData(dataBuf, 0, dataSize);
            ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
            DataInputStream in = new DataInputStream(baStream);
            byte[] inputBytes = new byte[dataSize];
            data.readEntityData(inputBytes, 0, dataSize);
            ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes);
            DataInputStream inputBufferStream = new DataInputStream(inputBuffer);

            if (key.equals(GLOBAL_METADATA_KEY)) {
                int storedSdkVersion = in.readInt();
                int storedSdkVersion = inputBufferStream.readInt();
                if (DEBUG) Slog.v(TAG, "   storedSystemVersion = " + storedSystemVersion);
                if (storedSystemVersion > Build.VERSION.SDK_INT) {
                    // returning before setting the sig map means we rejected the restore set
@@ -277,7 +278,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
                    return;
                }
                mStoredSdkVersion = storedSdkVersion;
                mStoredIncrementalVersion = in.readUTF();
                mStoredIncrementalVersion = inputBufferStream.readUTF();
                mHasMetadata = true;
                if (DEBUG) {
                    Slog.i(TAG, "Restore set version " + storedSystemVersion
@@ -287,14 +288,20 @@ public class PackageManagerBackupAgent extends BackupAgent {
                }
            } else {
                // it's a file metadata record
                int versionCode = in.readInt();
                Signature[] sigs = unflattenSignatureArray(in);
                int versionCode = inputBufferStream.readInt();
                Signature[] sigs = readSignatureArray(inputBufferStream);
                if (DEBUG) {
                    Slog.i(TAG, "   restored metadata for " + key
                    Slog.i(TAG, "   read metadata for " + key
                            + " dataSize=" + dataSize
                            + " versionCode=" + versionCode + " sigs=" + sigs);
                }
                
                if (sigs == null || sigs.length == 0) {
                    Slog.w(TAG, "Not restoring package " + key
                            + " since it appears to have no signatures.");
                    continue;
                }

                ApplicationInfo app = new ApplicationInfo();
                app.packageName = key;
                restoredApps.add(app);
@@ -306,37 +313,30 @@ public class PackageManagerBackupAgent extends BackupAgent {
        mRestoredSignatures = sigMap;
    }

    private static void writeSignatureArray(DataOutputStream out, Signature[] sigs)
            throws IOException {
        // write the number of signatures in the array
        out.writeInt(sigs.length);

    // Util: convert an array of Signatures into a flattened byte buffer.  The
    // flattened format contains enough info to reconstruct the signature array.
    private byte[] flattenSignatureArray(Signature[] allSigs) {
        ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(outBuf);

        // build the set of subsidiary buffers
        try {
            // first the # of signatures in the array
            out.writeInt(allSigs.length);

            // then the signatures themselves, length + flattened buffer
            for (Signature sig : allSigs) {
        // write the signatures themselves, length + flattened buffer
        for (Signature sig : sigs) {
            byte[] flat = sig.toByteArray();
            out.writeInt(flat.length);
            out.write(flat);
        }
        } catch (IOException e) {
            // very strange; we're writing to memory here.  abort.
            return null;
    }

        return outBuf.toByteArray();
    private static Signature[] readSignatureArray(DataInputStream in) {
        try {
            int num;
            try {
                num = in.readInt();
            } catch (EOFException e) {
                // clean termination
                Slog.w(TAG, "Read empty signature block");
                return null;
            }
            
    private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
        Signature[] sigs = null;

        try {
            int num = in.readInt();
            if (DEBUG) Slog.v(TAG, " ... unflatten read " + num);
            
            // Sensical?
@@ -345,24 +345,18 @@ public class PackageManagerBackupAgent extends BackupAgent {
                throw new IllegalStateException("Bad restore state");
            }
            
            sigs = new Signature[num];
            Signature[] sigs = new Signature[num];
            for (int i = 0; i < num; i++) {
                int len = in.readInt();
                byte[] flatSig = new byte[len];
                in.read(flatSig);
                sigs[i] = new Signature(flatSig);
            }
        } catch (EOFException e) {
            // clean termination
            if (sigs == null) {
                Slog.w(TAG, "Empty signature block found");
            }
            return sigs;
        } catch (IOException e) {
            Slog.e(TAG, "Unable to unflatten sigs");
            Slog.e(TAG, "Unable to read signatures");
            return null;
        }

        return sigs;
    }

    // Util: parse out an existing state file into a usable structure