15a77537f31715ac32112f47a569eded13dce5f1bJohn Reck/*
25a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * Copyright (C) 2010 The Android Open Source Project
35a77537f31715ac32112f47a569eded13dce5f1bJohn Reck *
45a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * Licensed under the Apache License, Version 2.0 (the "License");
55a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * you may not use this file except in compliance with the License.
65a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * You may obtain a copy of the License at
75a77537f31715ac32112f47a569eded13dce5f1bJohn Reck *
85a77537f31715ac32112f47a569eded13dce5f1bJohn Reck *      http://www.apache.org/licenses/LICENSE-2.0
95a77537f31715ac32112f47a569eded13dce5f1bJohn Reck *
105a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * Unless required by applicable law or agreed to in writing, software
115a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * distributed under the License is distributed on an "AS IS" BASIS,
125a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * See the License for the specific language governing permissions and
145a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * limitations under the License.
155a77537f31715ac32112f47a569eded13dce5f1bJohn Reck */
165a77537f31715ac32112f47a569eded13dce5f1bJohn Reck
175a77537f31715ac32112f47a569eded13dce5f1bJohn Reckpackage android.drm;
185a77537f31715ac32112f47a569eded13dce5f1bJohn Reck
195a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.io.BufferedInputStream;
205a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.io.File;
215a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.io.FileInputStream;
225a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.io.FileNotFoundException;
235a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.io.FileOutputStream;
245a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.io.IOException;
255a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.io.InputStream;
265a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.io.OutputStream;
275a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.util.HashMap;
285a77537f31715ac32112f47a569eded13dce5f1bJohn Reckimport java.util.Iterator;
295a77537f31715ac32112f47a569eded13dce5f1bJohn Reck
305a77537f31715ac32112f47a569eded13dce5f1bJohn Reck/**
315a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * A utility class that provides operations for parsing extended metadata embedded in
325a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * DRM constraint information. If a DRM scheme has specific constraints beyond the standard
335a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * constraints, the constraints will show up in the
345a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * {@link DrmStore.ConstraintsColumns#EXTENDED_METADATA} key. You can use
355a77537f31715ac32112f47a569eded13dce5f1bJohn Reck * {@link DrmUtils.ExtendedMetadataParser} to iterate over those values.
365a77537f31715ac32112f47a569eded13dce5f1bJohn Reck */
375a77537f31715ac32112f47a569eded13dce5f1bJohn Reckpublic class DrmUtils {
385a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    /* Should be used when we need to read from local file */
395a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    /* package */ static byte[] readBytes(String path) throws IOException {
405a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        File file = new File(path);
415a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        return readBytes(file);
425a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    }
435a77537f31715ac32112f47a569eded13dce5f1bJohn Reck
445a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    /* Should be used when we need to read from local file */
455a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    /* package */ static byte[] readBytes(File file) throws IOException {
465a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        FileInputStream inputStream = new FileInputStream(file);
475a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        BufferedInputStream bufferedStream = new BufferedInputStream(inputStream);
485a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        byte[] data = null;
49914c5591baeb86bf30a5bc28930071442a822d60Ben Murdoch
505a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        try {
515a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            int length = bufferedStream.available();
525a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            if (length > 0) {
535a77537f31715ac32112f47a569eded13dce5f1bJohn Reck                data = new byte[length];
545a77537f31715ac32112f47a569eded13dce5f1bJohn Reck                // read the entire data
555a77537f31715ac32112f47a569eded13dce5f1bJohn Reck                bufferedStream.read(data);
565a77537f31715ac32112f47a569eded13dce5f1bJohn Reck             }
575a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        } finally {
585a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            quiteDispose(bufferedStream);
595a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            quiteDispose(inputStream);
605a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        }
615a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        return data;
625a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    }
635a77537f31715ac32112f47a569eded13dce5f1bJohn Reck
645a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    /* package */ static void writeToFile(final String path, byte[] data) throws IOException {
655a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        /* check for invalid inputs */
662b00b825565a166e9969df25597c1df75ee28dacKristian Monsen        FileOutputStream outputStream = null;
675a77537f31715ac32112f47a569eded13dce5f1bJohn Reck
685a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        if (null != path && null != data) {
695a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            try {
705a77537f31715ac32112f47a569eded13dce5f1bJohn Reck                outputStream = new FileOutputStream(path);
715a77537f31715ac32112f47a569eded13dce5f1bJohn Reck                outputStream.write(data);
725a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            } finally {
735a77537f31715ac32112f47a569eded13dce5f1bJohn Reck                quiteDispose(outputStream);
745a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            }
755a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        }
765a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    }
775a77537f31715ac32112f47a569eded13dce5f1bJohn Reck
785a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    /* package */ static void removeFile(String path) throws IOException {
795a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        File file = new File(path);
805a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        file.delete();
815a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    }
825a77537f31715ac32112f47a569eded13dce5f1bJohn Reck
835a77537f31715ac32112f47a569eded13dce5f1bJohn Reck    private static void quiteDispose(InputStream stream) {
845a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        try {
855a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            if (null != stream) {
865a77537f31715ac32112f47a569eded13dce5f1bJohn Reck                stream.close();
875a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            }
885a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        } catch (IOException e) {
895a77537f31715ac32112f47a569eded13dce5f1bJohn Reck            // no need to care, at least as of now
905a77537f31715ac32112f47a569eded13dce5f1bJohn Reck        }
91    }
92
93    private static void quiteDispose(OutputStream stream) {
94        try {
95            if (null != stream) {
96                stream.close();
97            }
98        } catch (IOException e) {
99            // no need to care
100        }
101    }
102
103    /**
104     * Gets an instance of {@link DrmUtils.ExtendedMetadataParser}, which can be used to parse
105     * extended metadata embedded in DRM constraint information.
106     *
107     * @param extendedMetadata Object in which key-value pairs of extended metadata are embedded.
108     *
109     */
110    public static ExtendedMetadataParser getExtendedMetadataParser(byte[] extendedMetadata) {
111        return new ExtendedMetadataParser(extendedMetadata);
112    }
113
114    /**
115     * Utility that parses extended metadata embedded in DRM constraint information.
116     *<p>
117     * Usage example:
118     *<p>
119     * byte[] extendedMetadata<br>
120     * &nbsp;&nbsp;&nbsp;&nbsp; =
121     *         constraints.getAsByteArray(DrmStore.ConstraintsColumns.EXTENDED_METADATA);<br>
122     * ExtendedMetadataParser parser = getExtendedMetadataParser(extendedMetadata);<br>
123     * Iterator keyIterator = parser.keyIterator();<br>
124     * while (keyIterator.hasNext()) {<br>
125     *     &nbsp;&nbsp;&nbsp;&nbsp;String extendedMetadataKey = keyIterator.next();<br>
126     *     &nbsp;&nbsp;&nbsp;&nbsp;String extendedMetadataValue =
127     *             parser.get(extendedMetadataKey);<br>
128     * }
129     */
130    public static class ExtendedMetadataParser {
131        HashMap<String, String> mMap = new HashMap<String, String>();
132
133        private int readByte(byte[] constraintData, int arrayIndex) {
134            //Convert byte[] into int.
135            return (int)constraintData[arrayIndex];
136        }
137
138        private String readMultipleBytes(
139                byte[] constraintData, int numberOfBytes, int arrayIndex) {
140            byte[] returnBytes = new byte[numberOfBytes];
141            for (int j = arrayIndex, i = 0; j < arrayIndex + numberOfBytes; j++,i++) {
142                returnBytes[i] = constraintData[j];
143            }
144            return new String(returnBytes);
145        }
146
147        /*
148         * This will parse the following format
149         * KeyLengthValueLengthKeyValueKeyLength1ValueLength1Key1Value1..\0
150         */
151        private ExtendedMetadataParser(byte[] constraintData) {
152            //Extract KeyValue Pair Info, till terminator occurs.
153            int index = 0;
154
155            while (index < constraintData.length) {
156                //Parse Key Length
157                int keyLength = readByte(constraintData, index);
158                index++;
159
160                //Parse Value Length
161                int valueLength = readByte(constraintData, index);
162                index++;
163
164                //Fetch key
165                String strKey = readMultipleBytes(constraintData, keyLength, index);
166                index += keyLength;
167
168                //Fetch Value
169                String strValue = readMultipleBytes(constraintData, valueLength, index);
170                if (strValue.equals(" ")) {
171                    strValue = "";
172                }
173                index += valueLength;
174                mMap.put(strKey, strValue);
175            }
176        }
177
178        public Iterator<String> iterator() {
179            return mMap.values().iterator();
180        }
181
182        public Iterator<String> keyIterator() {
183            return mMap.keySet().iterator();
184        }
185
186        public String get(String key) {
187            return mMap.get(key);
188        }
189    }
190}
191
192