summaryrefslogtreecommitdiffstats
path: root/gallerycommon/src/com/android/gallery3d/common/Fingerprint.java
diff options
context:
space:
mode:
Diffstat (limited to 'gallerycommon/src/com/android/gallery3d/common/Fingerprint.java')
-rw-r--r--gallerycommon/src/com/android/gallery3d/common/Fingerprint.java191
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));
+ }
+ }
+}