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
17package android.drm;
18
19import java.io.BufferedInputStream;
20import java.io.File;
21import java.io.FileInputStream;
22import java.io.FileOutputStream;
23import java.io.IOException;
24import java.io.InputStream;
25import java.io.OutputStream;
26import java.util.HashMap;
27import 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 */
36public class DrmUtils {
37    /* Should be used when we need to read from local file */
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 */
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
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
77    /* package */ static void removeFile(String path) throws IOException {
78        File file = new File(path);
79        file.delete();
80    }
81
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
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     */
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
132        private int readByte(byte[] constraintData, int arrayIndex) {
133            //Convert byte[] into int.
134            return (int)constraintData[arrayIndex];
135        }
136
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         */
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         */
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         */
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         */
205        public String get(String key) {
206            return mMap.get(key);
207        }
208    }
209}
210
211