am 75827d4a: Merge "Add direct API to get ManifestDigest" into jb-mr2-dev
* commit '75827d4a3155c190f455329a67c84ac8fbb9bda0': Add direct API to get ManifestDigest
This commit is contained in:
@@ -18,10 +18,17 @@ package android.content.pm;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Base64;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.jar.Attributes;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
/**
|
||||
* Represents the manifest digest for a package. This is suitable for comparison
|
||||
@@ -30,17 +37,17 @@ import java.util.jar.Attributes;
|
||||
* @hide
|
||||
*/
|
||||
public class ManifestDigest implements Parcelable {
|
||||
private static final String TAG = "ManifestDigest";
|
||||
|
||||
/** The digest of the manifest in our preferred order. */
|
||||
private final byte[] mDigest;
|
||||
|
||||
/** Digest field names to look for in preferred order. */
|
||||
private static final String[] DIGEST_TYPES = {
|
||||
"SHA1-Digest", "SHA-Digest", "MD5-Digest",
|
||||
};
|
||||
|
||||
/** What we print out first when toString() is called. */
|
||||
private static final String TO_STRING_PREFIX = "ManifestDigest {mDigest=";
|
||||
|
||||
/** Digest algorithm to use. */
|
||||
private static final String DIGEST_ALGORITHM = "SHA-256";
|
||||
|
||||
ManifestDigest(byte[] digest) {
|
||||
mDigest = digest;
|
||||
}
|
||||
@@ -49,26 +56,32 @@ public class ManifestDigest implements Parcelable {
|
||||
mDigest = source.createByteArray();
|
||||
}
|
||||
|
||||
static ManifestDigest fromAttributes(Attributes attributes) {
|
||||
if (attributes == null) {
|
||||
static ManifestDigest fromInputStream(InputStream fileIs) {
|
||||
if (fileIs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String encodedDigest = null;
|
||||
final MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance(DIGEST_ALGORITHM);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(DIGEST_ALGORITHM + " must be available", e);
|
||||
}
|
||||
|
||||
for (int i = 0; i < DIGEST_TYPES.length; i++) {
|
||||
final String value = attributes.getValue(DIGEST_TYPES[i]);
|
||||
if (value != null) {
|
||||
encodedDigest = value;
|
||||
break;
|
||||
final DigestInputStream dis = new DigestInputStream(new BufferedInputStream(fileIs), md);
|
||||
try {
|
||||
byte[] readBuffer = new byte[8192];
|
||||
while (dis.read(readBuffer, 0, readBuffer.length) != -1) {
|
||||
// not using
|
||||
}
|
||||
}
|
||||
|
||||
if (encodedDigest == null) {
|
||||
} catch (IOException e) {
|
||||
Slog.w(TAG, "Could not read manifest");
|
||||
return null;
|
||||
} finally {
|
||||
IoUtils.closeQuietly(dis);
|
||||
}
|
||||
|
||||
final byte[] digest = Base64.decode(encodedDigest, Base64.DEFAULT);
|
||||
final byte[] digest = md.digest();
|
||||
return new ManifestDigest(digest);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PatternMatcher;
|
||||
@@ -54,10 +53,9 @@ import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
@@ -567,6 +565,28 @@ public class PackageParser {
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the
|
||||
* APK. If it successfully scanned the package and found the
|
||||
* {@code AndroidManifest.xml}, {@code true} is returned.
|
||||
*/
|
||||
public boolean collectManifestDigest(Package pkg) {
|
||||
try {
|
||||
final JarFile jarFile = new JarFile(mArchiveSourcePath);
|
||||
try {
|
||||
final ZipEntry je = jarFile.getEntry(ANDROID_MANIFEST_FILENAME);
|
||||
if (je != null) {
|
||||
pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je));
|
||||
}
|
||||
} finally {
|
||||
jarFile.close();
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean collectCertificates(Package pkg, int flags) {
|
||||
pkg.mSignatures = null;
|
||||
|
||||
@@ -618,7 +638,6 @@ public class PackageParser {
|
||||
}
|
||||
} else {
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
final Manifest manifest = jarFile.getManifest();
|
||||
while (entries.hasMoreElements()) {
|
||||
final JarEntry je = entries.nextElement();
|
||||
if (je.isDirectory()) continue;
|
||||
@@ -629,8 +648,8 @@ public class PackageParser {
|
||||
continue;
|
||||
|
||||
if (ANDROID_MANIFEST_FILENAME.equals(name)) {
|
||||
final Attributes attributes = manifest.getAttributes(name);
|
||||
pkg.manifestDigest = ManifestDigest.fromAttributes(attributes);
|
||||
pkg.manifestDigest =
|
||||
ManifestDigest.fromInputStream(jarFile.getInputStream(je));
|
||||
}
|
||||
|
||||
final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer);
|
||||
|
||||
@@ -18,64 +18,51 @@ package android.content.pm;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.util.Base64;
|
||||
|
||||
import java.util.jar.Attributes;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class ManifestDigestTest extends AndroidTestCase {
|
||||
private static final byte[] DIGEST_1 = {
|
||||
private static final byte[] MESSAGE_1 = {
|
||||
(byte) 0x00, (byte) 0xAA, (byte) 0x55, (byte) 0xFF
|
||||
};
|
||||
|
||||
private static final String DIGEST_1_STR = Base64.encodeToString(DIGEST_1, Base64.DEFAULT);
|
||||
|
||||
private static final byte[] DIGEST_2 = {
|
||||
(byte) 0x0A, (byte) 0xA5, (byte) 0xF0, (byte) 0x5A
|
||||
};
|
||||
|
||||
private static final String DIGEST_2_STR = Base64.encodeToString(DIGEST_2, Base64.DEFAULT);
|
||||
|
||||
private static final Attributes.Name SHA1_DIGEST = new Attributes.Name("SHA1-Digest");
|
||||
|
||||
private static final Attributes.Name MD5_DIGEST = new Attributes.Name("MD5-Digest");
|
||||
|
||||
public void testManifestDigest_FromAttributes_Null() {
|
||||
public void testManifestDigest_FromInputStream_Null() {
|
||||
assertNull("Attributes were null, so ManifestDigest.fromAttributes should return null",
|
||||
ManifestDigest.fromAttributes(null));
|
||||
ManifestDigest.fromInputStream(null));
|
||||
}
|
||||
|
||||
public void testManifestDigest_FromAttributes_NoAttributes() {
|
||||
Attributes a = new Attributes();
|
||||
public void testManifestDigest_FromInputStream_ThrowsIoException() {
|
||||
InputStream is = new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
};
|
||||
|
||||
assertNull("There were no attributes to extract, so ManifestDigest should be null",
|
||||
ManifestDigest.fromAttributes(a));
|
||||
assertNull("InputStream threw exception, so ManifestDigest should be null",
|
||||
ManifestDigest.fromInputStream(is));
|
||||
}
|
||||
|
||||
public void testManifestDigest_FromAttributes_SHA1PreferredOverMD5() {
|
||||
Attributes a = new Attributes();
|
||||
a.put(SHA1_DIGEST, DIGEST_1_STR);
|
||||
public void testManifestDigest_Equals() throws Exception {
|
||||
InputStream is = new ByteArrayInputStream(MESSAGE_1);
|
||||
|
||||
a.put(MD5_DIGEST, DIGEST_2_STR);
|
||||
ManifestDigest expected =
|
||||
new ManifestDigest(MessageDigest.getInstance("SHA-256").digest(MESSAGE_1));
|
||||
|
||||
ManifestDigest fromAttributes = ManifestDigest.fromAttributes(a);
|
||||
ManifestDigest actual = ManifestDigest.fromInputStream(is);
|
||||
assertEquals(expected, actual);
|
||||
|
||||
assertNotNull("A valid ManifestDigest should be returned", fromAttributes);
|
||||
|
||||
ManifestDigest created = new ManifestDigest(DIGEST_1);
|
||||
|
||||
assertEquals("SHA-1 should be preferred over MD5: " + created.toString() + " vs. "
|
||||
+ fromAttributes.toString(), created, fromAttributes);
|
||||
|
||||
assertEquals("Hash codes should be the same: " + created.toString() + " vs. "
|
||||
+ fromAttributes.toString(), created.hashCode(), fromAttributes
|
||||
.hashCode());
|
||||
ManifestDigest unexpected = new ManifestDigest(new byte[0]);
|
||||
assertFalse(unexpected.equals(actual));
|
||||
}
|
||||
|
||||
public void testManifestDigest_Parcel() {
|
||||
Attributes a = new Attributes();
|
||||
a.put(SHA1_DIGEST, DIGEST_1_STR);
|
||||
public void testManifestDigest_Parcel() throws Exception {
|
||||
InputStream is = new ByteArrayInputStream(MESSAGE_1);
|
||||
|
||||
ManifestDigest digest = ManifestDigest.fromAttributes(a);
|
||||
ManifestDigest digest = ManifestDigest.fromInputStream(is);
|
||||
|
||||
Parcel p = Parcel.obtain();
|
||||
digest.writeToParcel(p, 0);
|
||||
|
||||
Reference in New Issue
Block a user