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.FileNotFoundException;
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 {
58d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            quiteDispose(bufferedStream);
59d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            quiteDispose(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 {
73d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                quiteDispose(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
83d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    private static void quiteDispose(InputStream stream) {
84d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        try {
85d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            if (null != stream) {
86d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                stream.close();
87d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            }
88d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        } catch (IOException e) {
89d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            // no need to care, at least as of now
90d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        }
91d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    }
92d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
93d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    private static void quiteDispose(OutputStream stream) {
94d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        try {
95d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            if (null != stream) {
96d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                stream.close();
97d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            }
98d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        } catch (IOException e) {
99d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            // no need to care
100d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        }
101d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    }
102d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
103d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    /**
1040e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber     * Gets an instance of {@link DrmUtils.ExtendedMetadataParser}, which can be used to parse
1050e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber     * extended metadata embedded in DRM constraint information.
106d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     *
1070e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber     * @param extendedMetadata Object in which key-value pairs of extended metadata are embedded.
108d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     *
109d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     */
110d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    public static ExtendedMetadataParser getExtendedMetadataParser(byte[] extendedMetadata) {
111d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        return new ExtendedMetadataParser(extendedMetadata);
112d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    }
113d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
114d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    /**
1150e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber     * Utility that parses extended metadata embedded in DRM constraint information.
1160e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber     *<p>
1170e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber     * Usage example:
1180e092f806b0a4b81785a52da8ba22d2d47087de5Bill Gruber     *<p>
119d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     * byte[] extendedMetadata<br>
120d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     * &nbsp;&nbsp;&nbsp;&nbsp; =
121d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     *         constraints.getAsByteArray(DrmStore.ConstraintsColumns.EXTENDED_METADATA);<br>
122d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     * ExtendedMetadataParser parser = getExtendedMetadataParser(extendedMetadata);<br>
123d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     * Iterator keyIterator = parser.keyIterator();<br>
124d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     * while (keyIterator.hasNext()) {<br>
125d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     *     &nbsp;&nbsp;&nbsp;&nbsp;String extendedMetadataKey = keyIterator.next();<br>
126d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     *     &nbsp;&nbsp;&nbsp;&nbsp;String extendedMetadataValue =
127d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     *             parser.get(extendedMetadataKey);<br>
128d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     * }
129d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi     */
130d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    public static class ExtendedMetadataParser {
131d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        HashMap<String, String> mMap = new HashMap<String, String>();
132d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
133d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        private int readByte(byte[] constraintData, int arrayIndex) {
134d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            //Convert byte[] into int.
135d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            return (int)constraintData[arrayIndex];
136d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        }
137d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
138d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        private String readMultipleBytes(
139d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                byte[] constraintData, int numberOfBytes, int arrayIndex) {
140d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            byte[] returnBytes = new byte[numberOfBytes];
141d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            for (int j = arrayIndex, i = 0; j < arrayIndex + numberOfBytes; j++,i++) {
142d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                returnBytes[i] = constraintData[j];
143d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            }
144d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            return new String(returnBytes);
145d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        }
146d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
147d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        /*
148d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi         * This will parse the following format
149d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi         * KeyLengthValueLengthKeyValueKeyLength1ValueLength1Key1Value1..\0
150d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi         */
151d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        private ExtendedMetadataParser(byte[] constraintData) {
152d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            //Extract KeyValue Pair Info, till terminator occurs.
153d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            int index = 0;
154d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
155d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            while (index < constraintData.length) {
156d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                //Parse Key Length
157d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                int keyLength = readByte(constraintData, index);
158d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                index++;
159d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
160d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                //Parse Value Length
161d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                int valueLength = readByte(constraintData, index);
162d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                index++;
163d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
164d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                //Fetch key
165d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                String strKey = readMultipleBytes(constraintData, keyLength, index);
166d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                index += keyLength;
167d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
168d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                //Fetch Value
169d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                String strValue = readMultipleBytes(constraintData, valueLength, index);
170c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi                if (strValue.equals(" ")) {
171c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi                    strValue = "";
172c7b3ccc564448cb4b918728421f9402bc18278c5Takeshi Aimi                }
173d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                index += valueLength;
174d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi                mMap.put(strKey, strValue);
175d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            }
176d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        }
177d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
178d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        public Iterator<String> iterator() {
179d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            return mMap.values().iterator();
180d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        }
181d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
182d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        public Iterator<String> keyIterator() {
183d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            return mMap.keySet().iterator();
184d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        }
185d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
186d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        public String get(String key) {
187d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi            return mMap.get(key);
188d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi        }
189d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi    }
190d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi}
191d074e30ce44b9e33da43b67a4515b8986ca72b26aimitakeshi
192