Workaround NPE caused by packages missing signatures.
Bug: b/2547993 Change-Id: Idcd4fc3ee4c2560a00a952e1910a50b30b736114
This commit is contained in:
@@ -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,49 +176,46 @@ 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 (doBackup) {
|
||||
// We need to store this app's metadata
|
||||
/*
|
||||
* Metadata for each package:
|
||||
*
|
||||
* int version -- [4] the package's versionCode
|
||||
* byte[] signatures -- [len] flattened Signature[] of the package
|
||||
*/
|
||||
|
||||
// marshal the version code in a canonical form
|
||||
bufStream.reset();
|
||||
outWriter.writeInt(info.versionCode);
|
||||
byte[] versionBuf = bufStream.toByteArray();
|
||||
|
||||
byte[] sigs = flattenSignatureArray(info.signatures);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "+ metadata for " + packName
|
||||
+ " version=" + info.versionCode
|
||||
+ " versionLen=" + versionBuf.length
|
||||
+ " sigsLen=" + sigs.length);
|
||||
if (info.versionCode == mStateVersions.get(packName).versionCode) {
|
||||
continue;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (info.signatures == null || info.signatures.length == 0)
|
||||
{
|
||||
Slog.w(TAG, "Not backing up package " + packName
|
||||
+ " since it appears to have no signatures.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to store this app's metadata
|
||||
/*
|
||||
* Metadata for each package:
|
||||
*
|
||||
* int version -- [4] the package's versionCode
|
||||
* byte[] signatures -- [len] flattened Signature[] of the package
|
||||
*/
|
||||
|
||||
// marshal the version code in a canonical form
|
||||
outputBuffer.reset();
|
||||
outputBufferStream.writeInt(info.versionCode);
|
||||
writeSignatureArray(outputBufferStream, info.signatures);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "+ writing metadata for " + packName
|
||||
+ " version=" + info.versionCode
|
||||
+ " entityLen=" + outputBuffer.size());
|
||||
}
|
||||
|
||||
// Now we can write the backup entity for this package
|
||||
writeEntity(data, packName, outputBuffer.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +240,12 @@ public class PackageManagerBackupAgent extends BackupAgent {
|
||||
// Finally, write the new state blob -- just the list of all apps we handled
|
||||
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
|
||||
@@ -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,13 +288,19 @@ 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;
|
||||
@@ -306,63 +313,50 @@ 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) {
|
||||
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;
|
||||
// write the signatures themselves, length + flattened buffer
|
||||
for (Signature sig : sigs) {
|
||||
byte[] flat = sig.toByteArray();
|
||||
out.writeInt(flat.length);
|
||||
out.write(flat);
|
||||
}
|
||||
|
||||
return outBuf.toByteArray();
|
||||
}
|
||||
|
||||
private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
|
||||
Signature[] sigs = null;
|
||||
|
||||
private static Signature[] readSignatureArray(DataInputStream in) {
|
||||
try {
|
||||
int num = in.readInt();
|
||||
int num;
|
||||
try {
|
||||
num = in.readInt();
|
||||
} catch (EOFException e) {
|
||||
// clean termination
|
||||
Slog.w(TAG, "Read empty signature block");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DEBUG) Slog.v(TAG, " ... unflatten read " + num);
|
||||
|
||||
|
||||
// Sensical?
|
||||
if (num > 20) {
|
||||
Slog.e(TAG, "Suspiciously large sig count in restore data; aborting");
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user