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     * &nbsp;&nbsp;&nbsp;&nbsp; =
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     *     &nbsp;&nbsp;&nbsp;&nbsp;String extendedMetadataKey = keyIterator.next();<br>
125d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     *     &nbsp;&nbsp;&nbsp;&nbsp;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