1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/* 2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2011 The Android Open Source Project 3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License. 6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at 7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * http://www.apache.org/licenses/LICENSE-2.0 9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software 11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and 14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License. 15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.common; 18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.ContentResolver; 20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.net.Uri; 21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.FileInputStream; 23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.IOException; 24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.InputStream; 25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.security.DigestInputStream; 26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.security.MessageDigest; 27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.security.NoSuchAlgorithmException; 28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.Arrays; 29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.List; 30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/** 32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * MD5-based digest Wrapper. 33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class Fingerprint { 35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Instance of the MessageDigest using our specified digest algorithm. 36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final MessageDigest DIGESTER; 37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin /** 39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Name of the digest algorithm we use in {@link java.security.MessageDigest} 40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final String DIGEST_MD5 = "md5"; 42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Version 1 streamId prefix. 44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Hard coded stream id length limit is 40-chars. Don't ask! 45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final String STREAM_ID_CS_PREFIX = "cs_01_"; 46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // 16 bytes for 128-bit fingerprint 48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int FINGERPRINT_BYTE_LENGTH; 49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // length of prefix + 32 hex chars for 128-bit fingerprint 51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int STREAM_ID_CS_01_LENGTH; 52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin static { 54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin DIGESTER = MessageDigest.getInstance(DIGEST_MD5); 56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin FINGERPRINT_BYTE_LENGTH = DIGESTER.getDigestLength(); 57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin STREAM_ID_CS_01_LENGTH = STREAM_ID_CS_PREFIX.length() 58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin + (FINGERPRINT_BYTE_LENGTH * 2); 59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (NoSuchAlgorithmException e) { 60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // can't continue, but really shouldn't happen 61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throw new IllegalStateException(e); 62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // md5 digest bytes. 66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private final byte[] mMd5Digest; 67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin /** 69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Creates a new Fingerprint. 70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public Fingerprint(byte[] bytes) { 72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if ((bytes == null) || (bytes.length != FINGERPRINT_BYTE_LENGTH)) { 73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throw new IllegalArgumentException(); 74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mMd5Digest = bytes; 76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin /** 79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Creates a Fingerprint based on the contents of a file. 80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Note that this will close() stream after calculating the digest. 82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * @param byteCount length of original data will be stored at byteCount[0] as a side product 83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * of the fingerprint calculation 84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static Fingerprint fromInputStream(InputStream stream, long[] byteCount) 86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throws IOException { 87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin DigestInputStream in = null; 88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin long count = 0; 89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin in = new DigestInputStream(stream, DIGESTER); 91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] bytes = new byte[8192]; 92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin while (true) { 93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // scan through file to compute a fingerprint. 94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int n = in.read(bytes); 95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (n < 0) break; 96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin count += n; 97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } finally { 99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (in != null) in.close(); 100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if ((byteCount != null) && (byteCount.length > 0)) byteCount[0] = count; 102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return new Fingerprint(in.getMessageDigest().digest()); 103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin /** 106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Decodes a string stream id to a 128-bit fingerprint. 107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static Fingerprint fromStreamId(String streamId) { 109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if ((streamId == null) 110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin || !streamId.startsWith(STREAM_ID_CS_PREFIX) 111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin || (streamId.length() != STREAM_ID_CS_01_LENGTH)) { 112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throw new IllegalArgumentException("bad streamId: " + streamId); 113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // decode the hex bytes of the fingerprint portion 116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] bytes = new byte[FINGERPRINT_BYTE_LENGTH]; 117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int byteIdx = 0; 118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int idx = STREAM_ID_CS_PREFIX.length(); idx < STREAM_ID_CS_01_LENGTH; 119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin idx += 2) { 120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int value = (toDigit(streamId, idx) << 4) | toDigit(streamId, idx + 1); 121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin bytes[byteIdx++] = (byte) (value & 0xff); 122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return new Fingerprint(bytes); 124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin /** 127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Scans a list of strings for a valid streamId. 128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * @param streamIdList list of stream id's to be scanned 130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * @return valid fingerprint or null if it can't be found 131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static Fingerprint extractFingerprint(List<String> streamIdList) { 133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (String streamId : streamIdList) { 134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (streamId.startsWith(STREAM_ID_CS_PREFIX)) { 135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return fromStreamId(streamId); 136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return null; 139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin /** 142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Encodes a 128-bit fingerprint as a string stream id. 143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Stream id string is limited to 40 characters, which could be digits, lower case ASCII and 145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * underscores. 146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public String toStreamId() { 148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin StringBuilder streamId = new StringBuilder(STREAM_ID_CS_PREFIX); 149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin appendHexFingerprint(streamId, mMd5Digest); 150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return streamId.toString(); 151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public byte[] getBytes() { 154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mMd5Digest; 155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean equals(Object obj) { 159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (this == obj) return true; 160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (!(obj instanceof Fingerprint)) return false; 161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Fingerprint other = (Fingerprint) obj; 162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return Arrays.equals(mMd5Digest, other.mMd5Digest); 163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean equals(byte[] md5Digest) { 166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return Arrays.equals(mMd5Digest, md5Digest); 167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin @Override 170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int hashCode() { 171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return Arrays.hashCode(mMd5Digest); 172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Utility methods. 175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static int toDigit(String streamId, int index) { 177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int digit = Character.digit(streamId.charAt(index), 16); 178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (digit < 0) { 179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throw new IllegalArgumentException("illegal hex digit in " + streamId); 180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return digit; 182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static void appendHexFingerprint(StringBuilder sb, byte[] bytes) { 185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int idx = 0; idx < FINGERPRINT_BYTE_LENGTH; idx++) { 186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int value = bytes[idx]; 187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin sb.append(Integer.toHexString((value >> 4) & 0x0f)); 188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin sb.append(Integer.toHexString(value& 0x0f)); 189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin} 192