1d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi/* 2d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * Copyright (C) 2010 The Android Open Source Project 3d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * 4d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * Licensed under the Apache License, Version 2.0 (the "License"); 5d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * you may not use this file except in compliance with the License. 6d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * You may obtain a copy of the License at 7d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * 8d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * http://www.apache.org/licenses/LICENSE-2.0 9d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * 10d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * Unless required by applicable law or agreed to in writing, software 11d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * distributed under the License is distributed on an "AS IS" BASIS, 12d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * See the License for the specific language governing permissions and 14d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * limitations under the License. 15d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 16d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 17d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshipackage android.drm; 18d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 19d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.BufferedInputStream; 20d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.File; 21d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.FileInputStream; 22d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.FileNotFoundException; 23d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.FileOutputStream; 24d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.IOException; 25d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.InputStream; 26d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.OutputStream; 27d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.util.HashMap; 28d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.util.Iterator; 29d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 30d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi/** 310e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * A utility class that provides operations for parsing extended metadata embedded in 320e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * DRM constraint information. If a DRM scheme has specific constraints beyond the standard 330e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * constraints, the constraints will show up in the 340e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * {@link DrmStore.ConstraintsColumns#EXTENDED_METADATA} key. You can use 350e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * {@link DrmUtils.ExtendedMetadataParser} to iterate over those values. 36d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 37d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshipublic class DrmUtils { 38d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* Should be used when we need to read from local file */ 39d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* package */ static byte[] readBytes(String path) throws IOException { 40d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi File file = new File(path); 41d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return readBytes(file); 42d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 43d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 44d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* Should be used when we need to read from local file */ 45d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* package */ static byte[] readBytes(File file) throws IOException { 46d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi FileInputStream inputStream = new FileInputStream(file); 47d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); 48d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi byte[] data = null; 49d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 50d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi try { 51d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi int length = bufferedStream.available(); 52d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi if (length > 0) { 53d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi data = new byte[length]; 54d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi // read the entire data 55d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi bufferedStream.read(data); 56d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 57d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } finally { 58d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi quiteDispose(bufferedStream); 59d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi quiteDispose(inputStream); 60d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 61d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return data; 62d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 63d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 64d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* package */ static void writeToFile(final String path, byte[] data) throws IOException { 65d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* check for invalid inputs */ 66d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi FileOutputStream outputStream = null; 67d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 68d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi if (null != path && null != data) { 69d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi try { 70d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi outputStream = new FileOutputStream(path); 71d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi outputStream.write(data); 72d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } finally { 73d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi quiteDispose(outputStream); 74d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 75d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 76d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 77d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 78d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* package */ static void removeFile(String path) throws IOException { 79d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi File file = new File(path); 80d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi file.delete(); 81d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 82d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 83d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi private static void quiteDispose(InputStream stream) { 84d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi try { 85d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi if (null != stream) { 86d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi stream.close(); 87d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 88d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } catch (IOException e) { 89d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi // no need to care, at least as of now 90d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 91d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 92d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 93d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi private static void quiteDispose(OutputStream stream) { 94d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi try { 95d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi if (null != stream) { 96d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi stream.close(); 97d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 98d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } catch (IOException e) { 99d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi // no need to care 100d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 101d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 102d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 103d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /** 1040e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * Gets an instance of {@link DrmUtils.ExtendedMetadataParser}, which can be used to parse 1050e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * extended metadata embedded in DRM constraint information. 106d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * 1070e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * @param extendedMetadata Object in which key-value pairs of extended metadata are embedded. 108d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * 109d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 110d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public static ExtendedMetadataParser getExtendedMetadataParser(byte[] extendedMetadata) { 111d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return new ExtendedMetadataParser(extendedMetadata); 112d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 113d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 114d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /** 1150e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * Utility that parses extended metadata embedded in DRM constraint information. 1160e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber *<p> 1170e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * Usage example: 1180e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber *<p> 119d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * byte[] extendedMetadata<br> 120d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * = 121d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * constraints.getAsByteArray(DrmStore.ConstraintsColumns.EXTENDED_METADATA);<br> 122d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * ExtendedMetadataParser parser = getExtendedMetadataParser(extendedMetadata);<br> 123d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * Iterator keyIterator = parser.keyIterator();<br> 124d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * while (keyIterator.hasNext()) {<br> 125d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * String extendedMetadataKey = keyIterator.next();<br> 126d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * String extendedMetadataValue = 127d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * parser.get(extendedMetadataKey);<br> 128d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * } 129d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 130d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public static class ExtendedMetadataParser { 131d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi HashMap<String, String> mMap = new HashMap<String, String>(); 132d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 133d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi private int readByte(byte[] constraintData, int arrayIndex) { 134d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Convert byte[] into int. 135d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return (int)constraintData[arrayIndex]; 136d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 137d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 138d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi private String readMultipleBytes( 139d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi byte[] constraintData, int numberOfBytes, int arrayIndex) { 140d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi byte[] returnBytes = new byte[numberOfBytes]; 141d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi for (int j = arrayIndex, i = 0; j < arrayIndex + numberOfBytes; j++,i++) { 142d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi returnBytes[i] = constraintData[j]; 143d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 144d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return new String(returnBytes); 145d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 146d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 147d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* 148d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * This will parse the following format 149d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * KeyLengthValueLengthKeyValueKeyLength1ValueLength1Key1Value1..\0 150d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 151d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi private ExtendedMetadataParser(byte[] constraintData) { 152d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Extract KeyValue Pair Info, till terminator occurs. 153d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi int index = 0; 154d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 155d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi while (index < constraintData.length) { 156d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Parse Key Length 157d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi int keyLength = readByte(constraintData, index); 158d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi index++; 159d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 160d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Parse Value Length 161d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi int valueLength = readByte(constraintData, index); 162d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi index++; 163d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 164d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Fetch key 165d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi String strKey = readMultipleBytes(constraintData, keyLength, index); 166d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi index += keyLength; 167d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 168d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Fetch Value 169d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi String strValue = readMultipleBytes(constraintData, valueLength, index); 170c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi if (strValue.equals(" ")) { 171c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi strValue = ""; 172c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi } 173d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi index += valueLength; 174d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi mMap.put(strKey, strValue); 175d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 176d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 177d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 178d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public Iterator<String> iterator() { 179d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return mMap.values().iterator(); 180d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 181d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 182d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public Iterator<String> keyIterator() { 183d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return mMap.keySet().iterator(); 184d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 185d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 186d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public String get(String key) { 187d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return mMap.get(key); 188d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 189d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 190d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi} 191d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 192