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.FileOutputStream; 23d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.IOException; 24d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.InputStream; 25d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.io.OutputStream; 26d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.util.HashMap; 27d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshiimport java.util.Iterator; 28d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 29d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi/** 300e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * A utility class that provides operations for parsing extended metadata embedded in 310e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * DRM constraint information. If a DRM scheme has specific constraints beyond the standard 320e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * constraints, the constraints will show up in the 330e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * {@link DrmStore.ConstraintsColumns#EXTENDED_METADATA} key. You can use 340e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * {@link DrmUtils.ExtendedMetadataParser} to iterate over those values. 35d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 36d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshipublic class DrmUtils { 37d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* Should be used when we need to read from local file */ 38d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* package */ static byte[] readBytes(String path) throws IOException { 39d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi File file = new File(path); 40d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return readBytes(file); 41d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 42d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 43d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* Should be used when we need to read from local file */ 44d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* package */ static byte[] readBytes(File file) throws IOException { 45d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi FileInputStream inputStream = new FileInputStream(file); 46d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi BufferedInputStream bufferedStream = new BufferedInputStream(inputStream); 47d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi byte[] data = null; 48d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 49d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi try { 50d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi int length = bufferedStream.available(); 51d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi if (length > 0) { 52d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi data = new byte[length]; 53d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi // read the entire data 54d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi bufferedStream.read(data); 55d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 56d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } finally { 57f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong quietlyDispose(bufferedStream); 58f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong quietlyDispose(inputStream); 59d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 60d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return data; 61d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 62d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 63d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* package */ static void writeToFile(final String path, byte[] data) throws IOException { 64d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* check for invalid inputs */ 65d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi FileOutputStream outputStream = null; 66d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 67d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi if (null != path && null != data) { 68d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi try { 69d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi outputStream = new FileOutputStream(path); 70d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi outputStream.write(data); 71d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } finally { 72f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong quietlyDispose(outputStream); 73d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 74d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 75d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 76d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 77d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* package */ static void removeFile(String path) throws IOException { 78d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi File file = new File(path); 79d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi file.delete(); 80d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 81d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 82f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong private static void quietlyDispose(InputStream stream) { 83d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi try { 84d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi if (null != stream) { 85d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi stream.close(); 86d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 87d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } catch (IOException e) { 88d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi // no need to care, at least as of now 89d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 90d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 91d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 92f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong private static void quietlyDispose(OutputStream stream) { 93d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi try { 94d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi if (null != stream) { 95d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi stream.close(); 96d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 97d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } catch (IOException e) { 98d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi // no need to care 99d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 100d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 101d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 102d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /** 1030e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * Gets an instance of {@link DrmUtils.ExtendedMetadataParser}, which can be used to parse 1040e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * extended metadata embedded in DRM constraint information. 105d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * 1060e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * @param extendedMetadata Object in which key-value pairs of extended metadata are embedded. 107d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * 108d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 109d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public static ExtendedMetadataParser getExtendedMetadataParser(byte[] extendedMetadata) { 110d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return new ExtendedMetadataParser(extendedMetadata); 111d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 112d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 113d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /** 1140e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * Utility that parses extended metadata embedded in DRM constraint information. 1150e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber *<p> 1160e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber * Usage example: 1170e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber *<p> 118d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * byte[] extendedMetadata<br> 119d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * = 120d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * constraints.getAsByteArray(DrmStore.ConstraintsColumns.EXTENDED_METADATA);<br> 121d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * ExtendedMetadataParser parser = getExtendedMetadataParser(extendedMetadata);<br> 122d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * Iterator keyIterator = parser.keyIterator();<br> 123d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * while (keyIterator.hasNext()) {<br> 124d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * String extendedMetadataKey = keyIterator.next();<br> 125d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * String extendedMetadataValue = 126d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * parser.get(extendedMetadataKey);<br> 127d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * } 128d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 129d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public static class ExtendedMetadataParser { 130d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi HashMap<String, String> mMap = new HashMap<String, String>(); 131d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 132d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi private int readByte(byte[] constraintData, int arrayIndex) { 133d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Convert byte[] into int. 134d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return (int)constraintData[arrayIndex]; 135d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 136d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 137d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi private String readMultipleBytes( 138d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi byte[] constraintData, int numberOfBytes, int arrayIndex) { 139d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi byte[] returnBytes = new byte[numberOfBytes]; 140d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi for (int j = arrayIndex, i = 0; j < arrayIndex + numberOfBytes; j++,i++) { 141d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi returnBytes[i] = constraintData[j]; 142d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 143d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return new String(returnBytes); 144d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 145d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 146d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi /* 147d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * This will parse the following format 148d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi * KeyLengthValueLengthKeyValueKeyLength1ValueLength1Key1Value1..\0 149d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi */ 150d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi private ExtendedMetadataParser(byte[] constraintData) { 151d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Extract KeyValue Pair Info, till terminator occurs. 152d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi int index = 0; 153d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 154d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi while (index < constraintData.length) { 155d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Parse Key Length 156d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi int keyLength = readByte(constraintData, index); 157d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi index++; 158d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 159d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Parse Value Length 160d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi int valueLength = readByte(constraintData, index); 161d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi index++; 162d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 163d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Fetch key 164d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi String strKey = readMultipleBytes(constraintData, keyLength, index); 165d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi index += keyLength; 166d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 167d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi //Fetch Value 168d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi String strValue = readMultipleBytes(constraintData, valueLength, index); 169c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi if (strValue.equals(" ")) { 170c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi strValue = ""; 171c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi } 172d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi index += valueLength; 173d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi mMap.put(strKey, strValue); 174d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 175d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 176d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 177f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong /** 178f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * This method returns an iterator object that can be used to iterate over 179f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * all values of the metadata. 180f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * 181f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * @return The iterator object. 182f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong */ 183d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public Iterator<String> iterator() { 184d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return mMap.values().iterator(); 185d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 186d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 187f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong /** 188f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * This method returns an iterator object that can be used to iterate over 189f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * all keys of the metadata. 190f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * 191f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * @return The iterator object. 192f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong */ 193d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public Iterator<String> keyIterator() { 194d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return mMap.keySet().iterator(); 195d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 196d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 197f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong /** 198f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * This method retrieves the metadata value associated with a given key. 199f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * 200f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * @param key The key whose value is being retrieved. 201f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * 202f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * @return The metadata value associated with the given key. Returns null 203f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong * if the key is not found. 204f7a68fc98550859019bb0636fc3e8d88cb50e6a6James Dong */ 205d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi public String get(String key) { 206d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi return mMap.get(key); 207d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 208d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi } 209d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi} 210d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi 211