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