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.Closeable;
21import java.io.File;
22import java.io.FileInputStream;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.io.InputStream;
26import java.io.OutputStream;
27import java.util.HashMap;
28import 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 */
37public class DrmUtils {
38    /* Should be used when we need to read from local file */
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 */
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
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
78    /* package */ static void removeFile(String path) throws IOException {
79        File file = new File(path);
80        file.delete();
81    }
82
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     */
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
123        private int readByte(byte[] constraintData, int arrayIndex) {
124            //Convert byte[] into int.
125            return (int)constraintData[arrayIndex];
126        }
127
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         */
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         */
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         */
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         */
196        public String get(String key) {
197            return mMap.get(key);
198        }
199    }
200}
201
202