1/*
2 * Copyright (C) 2007 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.mobile1;
18
19import java.io.*;
20
21/**
22 * This class provides interfaces to access the DRM raw content.
23 */
24public class DrmRawContent {
25    /**
26     * The "application/vnd.oma.drm.message" mime type.
27     */
28    public static final String DRM_MIMETYPE_MESSAGE_STRING = "application/vnd.oma.drm.message";
29
30    /**
31     * The "application/vnd.oma.drm.content" mime type.
32     */
33    public static final String DRM_MIMETYPE_CONTENT_STRING = "application/vnd.oma.drm.content";
34
35    /**
36     * The DRM delivery type: Forward-Lock
37     */
38    public static final int DRM_FORWARD_LOCK = 1;
39
40    /**
41     * The DRM delivery type: Combined Delivery
42     */
43    public static final int DRM_COMBINED_DELIVERY = 2;
44
45    /**
46     * The DRM delivery type: Separate Delivery
47     */
48    public static final int DRM_SEPARATE_DELIVERY = 3;
49
50    /**
51     * The DRM delivery type: Separate Delivery in DRM message
52     */
53    public static final int DRM_SEPARATE_DELIVERY_DM = 4;
54
55    /**
56     * The DRM media content length is unknown currently
57     */
58    public static final int DRM_UNKNOWN_DATA_LEN = -1;
59
60
61    /**
62     * The id of "application/vnd.oma.drm.message" mime type.
63     */
64    private static final int DRM_MIMETYPE_MESSAGE = 1;
65
66    /**
67     * The id of "application/vnd.oma.drm.content" mime type.
68     */
69    private static final int DRM_MIMETYPE_CONTENT = 2;
70
71    /**
72     * Successful operation.
73     */
74    private static final int JNI_DRM_SUCCESS = 0;
75
76    /**
77     * General failure.
78     */
79    private static final int JNI_DRM_FAILURE = -1;
80
81    /**
82     * Indicates the end of the DRM content is reached.
83     */
84    private static final int JNI_DRM_EOF = -2;
85
86    /**
87     * The media content length is unknown from native method
88     */
89    private static final int JNI_DRM_UNKNOWN_DATA_LEN = -3;
90
91    /**
92     * The member to save the original InputStream data.
93     */
94    private BufferedInputStream inData;
95
96    /**
97     * The member to save the original InputStream data length.
98     */
99    private int inDataLen;
100
101    /**
102     * The unique id to this DRM content. It will be initialized
103     * in constructor by native method. And it will not be changed
104     * after initialization.
105     */
106    private int id;
107
108    /**
109     * The rights issuer address of this DRM object.
110     */
111    private String rightsIssuer;
112
113    /**
114     * The media content type of this DRM object.
115     */
116    private String mediaType;
117
118    /**
119     * The delivery method type of this DRM object.
120     */
121    private int rawType;
122
123
124    /**
125     * Construct a DrmRawContent object.
126     *
127     * @param inRawdata     object of DRM raw data stream.
128     * @param len           the length of raw data can be read.
129     * @param mimeTypeStr   the mime type of the DRM content.
130     */
131    public DrmRawContent(InputStream inRawdata, int len, String mimeTypeStr) throws DrmException, IOException {
132        int mimeType;
133
134        id = -1;
135        inData = new BufferedInputStream(inRawdata, 1024);
136        inDataLen = len;
137
138        if (DRM_MIMETYPE_MESSAGE_STRING.equals(mimeTypeStr))
139            mimeType = DRM_MIMETYPE_MESSAGE;
140        else if (DRM_MIMETYPE_CONTENT_STRING.equals(mimeTypeStr))
141            mimeType = DRM_MIMETYPE_CONTENT;
142        else
143            throw new IllegalArgumentException("mimeType must be DRM_MIMETYPE_MESSAGE or DRM_MIMETYPE_CONTENT");
144
145        if (len <= 0)
146            throw new IllegalArgumentException("len must be > 0");
147
148        /* call native method to initialize this DRM content */
149        id = nativeConstructDrmContent(inData, inDataLen, mimeType);
150
151        if (JNI_DRM_FAILURE == id)
152            throw new DrmException("nativeConstructDrmContent() returned JNI_DRM_FAILURE");
153
154        /* init the rights issuer field. */
155        rightsIssuer = nativeGetRightsAddress();
156
157        /* init the raw content type. */
158        rawType = nativeGetDeliveryMethod();
159        if (JNI_DRM_FAILURE == rawType)
160            throw new DrmException("nativeGetDeliveryMethod() returned JNI_DRM_FAILURE");
161
162        /* init the media content type. */
163        mediaType = nativeGetContentType();
164        if (null == mediaType)
165            throw new DrmException("nativeGetContentType() returned null");
166    }
167
168    /**
169     * Get rights address from raw Seperate Delivery content.
170     *
171     * @return the string of the rights issuer address,
172     *         or null if no rights issuer.
173     */
174    public String getRightsAddress() {
175        return rightsIssuer;
176    }
177
178    /**
179     * Get the type of the raw DRM content.
180     *
181     * @return one of the following delivery type of this DRM content:
182     *              #DRM_FORWARD_LOCK
183     *              #DRM_COMBINED_DELIVERY
184     *              #DRM_SEPARATE_DELIVERY
185     *              #DRM_SEPARATE_DELIVERY_DM
186     */
187    public int getRawType() {
188        return rawType;
189    }
190
191    /**
192     * Get one InputStream object to read decrypted content.
193     *
194     * @param rights        the rights object contain decrypted key.
195     *
196     * @return the InputStream object of decrypted media content.
197     */
198    public InputStream getContentInputStream(DrmRights rights) {
199        if (null == rights)
200            throw new NullPointerException();
201
202        return new DrmInputStream(rights);
203    }
204
205    /**
206     * Get the type of the decrypted media content.
207     *
208     * @return the decrypted media content type of this DRM content.
209     */
210    public String getContentType() {
211        return mediaType;
212    }
213
214    /**
215     * Get the length of the decrypted media content.
216     *
217     * @param rights        the rights object contain decrypted key.
218     *
219     * @return the length of the decrypted media content.
220     *         #DRM_UNKNOWN_DATA_LEN if the length is unknown currently.
221     */
222    public int getContentLength(DrmRights rights) throws DrmException {
223        /**
224         * Because currently the media object associate with rights object
225         * has been handled in native logic, so here it is not need to deal
226         * the rights. But for the apps, it is mandatory for user to get
227         * the rights object before get the media content length.
228         */
229        if (null == rights)
230            throw new NullPointerException();
231
232        int mediaLen = nativeGetContentLength();
233
234        if (JNI_DRM_FAILURE == mediaLen)
235            throw new DrmException("nativeGetContentLength() returned JNI_DRM_FAILURE");
236
237        if (JNI_DRM_UNKNOWN_DATA_LEN == mediaLen)
238            return DRM_UNKNOWN_DATA_LEN;
239
240        return mediaLen;
241    }
242
243    /**
244     * This class provide a InputStream to the DRM media content.
245     */
246    class DrmInputStream extends InputStream
247    {
248        /**
249         * The flag to indicate whether this stream is closed or not.
250         */
251        private boolean isClosed;
252
253        /**
254         * The offset of this DRM content to be reset.
255         */
256        private int offset;
257
258        /**
259         * A byte of data to be readed.
260         */
261        private byte[] b;
262
263        /**
264         * Construct a DrmInputStream instance.
265         */
266        public DrmInputStream(DrmRights rights) {
267            /**
268             * Because currently the media object associate with rights object
269             * has been handled in native logic, so here it is not need to deal
270             * the rights. But for the apps, it is mandatory for user to get
271             * the rights object before get the media content data.
272             */
273
274            isClosed = false;
275            offset = 0;
276            b = new byte[1];
277        }
278
279        /* Non-javadoc
280         * @see java.io.InputStream#available()
281         */
282        public int available() throws IOException {
283            /* call native method to get this DRM decrypted media content length */
284            int len = nativeGetContentLength();
285
286            if (JNI_DRM_FAILURE == len)
287                throw new IOException();
288
289            /* if the length is unknown, just return 0 for available value */
290            if (JNI_DRM_UNKNOWN_DATA_LEN == len)
291                return 0;
292
293            int availableLen = len - offset;
294            if (availableLen < 0)
295                throw new IOException();
296
297            return availableLen;
298        }
299
300        /* Non-javadoc
301         * @see java.io.InputStream#read()
302         */
303        public int read() throws IOException {
304            int res;
305
306            res = read(b, 0, 1);
307
308            if (-1 == res)
309                return -1;
310
311            return b[0] & 0xff;
312        }
313
314        /* Non-javadoc
315         * @see java.io.InputStream#read(byte)
316         */
317        public int read(byte[] b) throws IOException {
318            return read(b, 0, b.length);
319        }
320
321        /* Non-javadoc
322         * @see java.io.InputStream#read(byte, int, int)
323         */
324        public int read(byte[] b, int off, int len) throws IOException {
325            if (null == b)
326                throw new NullPointerException();
327            if (off < 0 || len < 0 || off + len > b.length)
328                throw new IndexOutOfBoundsException();
329            if (true == isClosed)
330                throw new IOException();
331
332            if (0 == len)
333                return 0;
334
335            len = nativeReadContent(b, off, len, offset);
336
337            if (JNI_DRM_FAILURE == len)
338                throw new IOException();
339            else if (JNI_DRM_EOF == len)
340                return -1;
341
342            offset += len;
343
344            return len;
345        }
346
347        /* Non-javadoc
348         * @see java.io.InputStream#markSupported()
349         */
350        public boolean markSupported() {
351            return false;
352        }
353
354        /* Non-javadoc
355         * @see java.io.InputStream#mark(int)
356         */
357        public void mark(int readlimit) {
358        }
359
360        /* Non-javadoc
361         * @see java.io.InputStream#reset()
362         */
363        public void reset() throws IOException {
364            throw new IOException();
365        }
366
367        /* Non-javadoc
368         * @see java.io.InputStream#skip()
369         */
370        public long skip(long n) throws IOException {
371            return 0;
372        }
373
374        /* Non-javadoc
375         * @see java.io.InputStream#close()
376         */
377        public void close() {
378            isClosed = true;
379        }
380    }
381
382    /**
383     * native method: construct a DRM content according the mime type.
384     *
385     * @param data      input DRM content data to be parsed.
386     * @param len       the length of the data.
387     * @param mimeType  the mime type of this DRM content. the value of this field includes:
388     *                      #DRM_MIMETYPE_MESSAGE
389     *                      #DRM_MIMETYPE_CONTENT
390     *
391     * @return #the id of the DRM content if succeed.
392     *         #JNI_DRM_FAILURE if fail.
393     */
394    private native int nativeConstructDrmContent(InputStream data, int len, int mimeType);
395
396    /**
397     * native method: get this DRM content rights issuer.
398     *
399     * @return the address of rights issuer if in case of separate delivery.
400     *         null if not separete delivery, or otherwise.
401     */
402    private native String nativeGetRightsAddress();
403
404    /**
405     * native method: get this DRM content delivery type.
406     *
407     * @return the delivery method, the value may be one of the following:
408     *              #DRM_FORWARD_LOCK
409     *              #DRM_COMBINED_DELIVERY
410     *              #DRM_SEPARATE_DELIVERY
411     *              #DRM_SEPARATE_DELIVERY_DM
412     *         #JNI_DRM_FAILURE if fail.
413     */
414    private native int nativeGetDeliveryMethod();
415
416    /**
417     * native method: get a piece of media content data.
418     *
419     * @param buf       the buffer to save DRM media content data.
420     * @param bufOff    the offset of the buffer to start to save data.
421     * @param len       the number of byte to read.
422     * @param mediaOff  the offset of the media content data to start to read.
423     *
424     * @return the length of the media content data has been read.
425     *         #JNI_DRM_EOF if reach to end of the media content.
426     *         #JNI_DRM_FAILURE if fail.
427     */
428    private native int nativeReadContent(byte[] buf, int bufOff, int len, int mediaOff);
429
430    /**
431     * native method: get this DRM content type.
432     *
433     * @return the decrypted media content type.
434     *         null if fail.
435     */
436    private native String nativeGetContentType();
437
438    /**
439     * native method: get this DRM decrypted media content length.
440     *
441     * @return the length of decrypted media content.
442     *         #JNI_DRM_FAILURE if fail.
443     *         #JNI_DRM_UNKNOWN_DATA_LEN if the length is unknown currently.
444     */
445    private native int nativeGetContentLength();
446
447    /**
448     * The finalizer of the DRMRawContent. Do some cleanup.
449     */
450    protected native void finalize();
451
452
453    /**
454     * Load the shared library to link the native methods.
455     */
456    static {
457        try {
458            System.loadLibrary("drm1_jni");
459        }
460        catch (UnsatisfiedLinkError ule) {
461            System.err.println("WARNING: Could not load libdrm1_jni.so");
462        }
463    }
464}
465