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