diff options
Diffstat (limited to 'gallerycommon/src/com/android/gallery3d/common/Fingerprint.java')
-rw-r--r-- | gallerycommon/src/com/android/gallery3d/common/Fingerprint.java | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/gallerycommon/src/com/android/gallery3d/common/Fingerprint.java b/gallerycommon/src/com/android/gallery3d/common/Fingerprint.java new file mode 100644 index 000000000..39fcf9e09 --- /dev/null +++ b/gallerycommon/src/com/android/gallery3d/common/Fingerprint.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.common; + +import android.content.ContentResolver; +import android.net.Uri; + +import java.io.FileInputStream; +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.List; + +/** + * MD5-based digest Wrapper. + */ +public class Fingerprint { + // Instance of the MessageDigest using our specified digest algorithm. + private static final MessageDigest DIGESTER; + + /** + * Name of the digest algorithm we use in {@link java.security.MessageDigest} + */ + private static final String DIGEST_MD5 = "md5"; + + // Version 1 streamId prefix. + // Hard coded stream id length limit is 40-chars. Don't ask! + private static final String STREAM_ID_CS_PREFIX = "cs_01_"; + + // 16 bytes for 128-bit fingerprint + private static final int FINGERPRINT_BYTE_LENGTH; + + // length of prefix + 32 hex chars for 128-bit fingerprint + private static final int STREAM_ID_CS_01_LENGTH; + + static { + try { + DIGESTER = MessageDigest.getInstance(DIGEST_MD5); + FINGERPRINT_BYTE_LENGTH = DIGESTER.getDigestLength(); + STREAM_ID_CS_01_LENGTH = STREAM_ID_CS_PREFIX.length() + + (FINGERPRINT_BYTE_LENGTH * 2); + } catch (NoSuchAlgorithmException e) { + // can't continue, but really shouldn't happen + throw new IllegalStateException(e); + } + } + + // md5 digest bytes. + private final byte[] mMd5Digest; + + /** + * Creates a new Fingerprint. + */ + public Fingerprint(byte[] bytes) { + if ((bytes == null) || (bytes.length != FINGERPRINT_BYTE_LENGTH)) { + throw new IllegalArgumentException(); + } + mMd5Digest = bytes; + } + + /** + * Creates a Fingerprint based on the contents of a file. + * + * Note that this will close() stream after calculating the digest. + * @param byteCount length of original data will be stored at byteCount[0] as a side product + * of the fingerprint calculation + */ + public static Fingerprint fromInputStream(InputStream stream, long[] byteCount) + throws IOException { + DigestInputStream in = null; + long count = 0; + try { + in = new DigestInputStream(stream, DIGESTER); + byte[] bytes = new byte[8192]; + while (true) { + // scan through file to compute a fingerprint. + int n = in.read(bytes); + if (n < 0) break; + count += n; + } + } finally { + if (in != null) in.close(); + } + if ((byteCount != null) && (byteCount.length > 0)) byteCount[0] = count; + return new Fingerprint(in.getMessageDigest().digest()); + } + + /** + * Decodes a string stream id to a 128-bit fingerprint. + */ + public static Fingerprint fromStreamId(String streamId) { + if ((streamId == null) + || !streamId.startsWith(STREAM_ID_CS_PREFIX) + || (streamId.length() != STREAM_ID_CS_01_LENGTH)) { + throw new IllegalArgumentException("bad streamId: " + streamId); + } + + // decode the hex bytes of the fingerprint portion + byte[] bytes = new byte[FINGERPRINT_BYTE_LENGTH]; + int byteIdx = 0; + for (int idx = STREAM_ID_CS_PREFIX.length(); idx < STREAM_ID_CS_01_LENGTH; + idx += 2) { + int value = (toDigit(streamId, idx) << 4) | toDigit(streamId, idx + 1); + bytes[byteIdx++] = (byte) (value & 0xff); + } + return new Fingerprint(bytes); + } + + /** + * Scans a list of strings for a valid streamId. + * + * @param streamIdList list of stream id's to be scanned + * @return valid fingerprint or null if it can't be found + */ + public static Fingerprint extractFingerprint(List<String> streamIdList) { + for (String streamId : streamIdList) { + if (streamId.startsWith(STREAM_ID_CS_PREFIX)) { + return fromStreamId(streamId); + } + } + return null; + } + + /** + * Encodes a 128-bit fingerprint as a string stream id. + * + * Stream id string is limited to 40 characters, which could be digits, lower case ASCII and + * underscores. + */ + public String toStreamId() { + StringBuilder streamId = new StringBuilder(STREAM_ID_CS_PREFIX); + appendHexFingerprint(streamId, mMd5Digest); + return streamId.toString(); + } + + public byte[] getBytes() { + return mMd5Digest; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Fingerprint)) return false; + Fingerprint other = (Fingerprint) obj; + return Arrays.equals(mMd5Digest, other.mMd5Digest); + } + + public boolean equals(byte[] md5Digest) { + return Arrays.equals(mMd5Digest, md5Digest); + } + + @Override + public int hashCode() { + return Arrays.hashCode(mMd5Digest); + } + + // Utility methods. + + private static int toDigit(String streamId, int index) { + int digit = Character.digit(streamId.charAt(index), 16); + if (digit < 0) { + throw new IllegalArgumentException("illegal hex digit in " + streamId); + } + return digit; + } + + private static void appendHexFingerprint(StringBuilder sb, byte[] bytes) { + for (int idx = 0; idx < FINGERPRINT_BYTE_LENGTH; idx++) { + int value = bytes[idx]; + sb.append(Integer.toHexString((value >> 4) & 0x0f)); + sb.append(Integer.toHexString(value& 0x0f)); + } + } +} |