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