/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.drm.mobile1; import java.io.*; /** * This class provides interfaces to access the DRM raw content. */ public class DrmRawContent { /** * The "application/vnd.oma.drm.message" mime type. */ public static final String DRM_MIMETYPE_MESSAGE_STRING = "application/vnd.oma.drm.message"; /** * The "application/vnd.oma.drm.content" mime type. */ public static final String DRM_MIMETYPE_CONTENT_STRING = "application/vnd.oma.drm.content"; /** * The DRM delivery type: Forward-Lock */ public static final int DRM_FORWARD_LOCK = 1; /** * The DRM delivery type: Combined Delivery */ public static final int DRM_COMBINED_DELIVERY = 2; /** * The DRM delivery type: Separate Delivery */ public static final int DRM_SEPARATE_DELIVERY = 3; /** * The DRM delivery type: Separate Delivery in DRM message */ public static final int DRM_SEPARATE_DELIVERY_DM = 4; /** * The DRM media content length is unknown currently */ public static final int DRM_UNKNOWN_DATA_LEN = -1; /** * The id of "application/vnd.oma.drm.message" mime type. */ private static final int DRM_MIMETYPE_MESSAGE = 1; /** * The id of "application/vnd.oma.drm.content" mime type. */ private static final int DRM_MIMETYPE_CONTENT = 2; /** * Successful operation. */ private static final int JNI_DRM_SUCCESS = 0; /** * General failure. */ private static final int JNI_DRM_FAILURE = -1; /** * Indicates the end of the DRM content is reached. */ private static final int JNI_DRM_EOF = -2; /** * The media content length is unknown from native method */ private static final int JNI_DRM_UNKNOWN_DATA_LEN = -3; /** * The member to save the original InputStream data. */ private BufferedInputStream inData; /** * The member to save the original InputStream data length. */ private int inDataLen; /** * The unique id to this DRM content. It will be initialized * in constructor by native method. And it will not be changed * after initialization. */ private int id; /** * The rights issuer address of this DRM object. */ private String rightsIssuer; /** * The media content type of this DRM object. */ private String mediaType; /** * The delivery method type of this DRM object. */ private int rawType; /** * Construct a DrmRawContent object. * * @param inRawdata object of DRM raw data stream. * @param len the length of raw data can be read. * @param mimeTypeStr the mime type of the DRM content. */ public DrmRawContent(InputStream inRawdata, int len, String mimeTypeStr) throws DrmException, IOException { int mimeType; id = -1; inData = new BufferedInputStream(inRawdata, 1024); inDataLen = len; if (DRM_MIMETYPE_MESSAGE_STRING.equals(mimeTypeStr)) mimeType = DRM_MIMETYPE_MESSAGE; else if (DRM_MIMETYPE_CONTENT_STRING.equals(mimeTypeStr)) mimeType = DRM_MIMETYPE_CONTENT; else throw new IllegalArgumentException("mimeType must be DRM_MIMETYPE_MESSAGE or DRM_MIMETYPE_CONTENT"); if (len <= 0) throw new IllegalArgumentException("len must be > 0"); /* call native method to initialize this DRM content */ id = nativeConstructDrmContent(inData, inDataLen, mimeType); if (JNI_DRM_FAILURE == id) throw new DrmException("nativeConstructDrmContent() returned JNI_DRM_FAILURE"); /* init the rights issuer field. */ rightsIssuer = nativeGetRightsAddress(); /* init the raw content type. */ rawType = nativeGetDeliveryMethod(); if (JNI_DRM_FAILURE == rawType) throw new DrmException("nativeGetDeliveryMethod() returned JNI_DRM_FAILURE"); /* init the media content type. */ mediaType = nativeGetContentType(); if (null == mediaType) throw new DrmException("nativeGetContentType() returned null"); } /** * Get rights address from raw Seperate Delivery content. * * @return the string of the rights issuer address, * or null if no rights issuer. */ public String getRightsAddress() { return rightsIssuer; } /** * Get the type of the raw DRM content. * * @return one of the following delivery type of this DRM content: * #DRM_FORWARD_LOCK * #DRM_COMBINED_DELIVERY * #DRM_SEPARATE_DELIVERY * #DRM_SEPARATE_DELIVERY_DM */ public int getRawType() { return rawType; } /** * Get one InputStream object to read decrypted content. * * @param rights the rights object contain decrypted key. * * @return the InputStream object of decrypted media content. */ public InputStream getContentInputStream(DrmRights rights) { if (null == rights) throw new NullPointerException(); return new DrmInputStream(rights); } /** * Get the type of the decrypted media content. * * @return the decrypted media content type of this DRM content. */ public String getContentType() { return mediaType; } /** * Get the length of the decrypted media content. * * @param rights the rights object contain decrypted key. * * @return the length of the decrypted media content. * #DRM_UNKNOWN_DATA_LEN if the length is unknown currently. */ public int getContentLength(DrmRights rights) throws DrmException { /** * Because currently the media object associate with rights object * has been handled in native logic, so here it is not need to deal * the rights. But for the apps, it is mandatory for user to get * the rights object before get the media content length. */ if (null == rights) throw new NullPointerException(); int mediaLen = nativeGetContentLength(); if (JNI_DRM_FAILURE == mediaLen) throw new DrmException("nativeGetContentLength() returned JNI_DRM_FAILURE"); if (JNI_DRM_UNKNOWN_DATA_LEN == mediaLen) return DRM_UNKNOWN_DATA_LEN; return mediaLen; } /** * This class provide a InputStream to the DRM media content. */ class DrmInputStream extends InputStream { /** * The flag to indicate whether this stream is closed or not. */ private boolean isClosed; /** * The offset of this DRM content to be reset. */ private int offset; /** * A byte of data to be readed. */ private byte[] b; /** * Construct a DrmInputStream instance. */ public DrmInputStream(DrmRights rights) { /** * Because currently the media object associate with rights object * has been handled in native logic, so here it is not need to deal * the rights. But for the apps, it is mandatory for user to get * the rights object before get the media content data. */ isClosed = false; offset = 0; b = new byte[1]; } /* Non-javadoc * @see java.io.InputStream#available() */ public int available() throws IOException { /* call native method to get this DRM decrypted media content length */ int len = nativeGetContentLength(); if (JNI_DRM_FAILURE == len) throw new IOException(); /* if the length is unknown, just return 0 for available value */ if (JNI_DRM_UNKNOWN_DATA_LEN == len) return 0; int availableLen = len - offset; if (availableLen < 0) throw new IOException(); return availableLen; } /* Non-javadoc * @see java.io.InputStream#read() */ public int read() throws IOException { int res; res = read(b, 0, 1); if (-1 == res) return -1; return b[0] & 0xff; } /* Non-javadoc * @see java.io.InputStream#read(byte) */ public int read(byte[] b) throws IOException { return read(b, 0, b.length); } /* Non-javadoc * @see java.io.InputStream#read(byte, int, int) */ public int read(byte[] b, int off, int len) throws IOException { if (null == b) throw new NullPointerException(); if (off < 0 || len < 0 || off + len > b.length) throw new IndexOutOfBoundsException(); if (true == isClosed) throw new IOException(); if (0 == len) return 0; len = nativeReadContent(b, off, len, offset); if (JNI_DRM_FAILURE == len) throw new IOException(); else if (JNI_DRM_EOF == len) return -1; offset += len; return len; } /* Non-javadoc * @see java.io.InputStream#markSupported() */ public boolean markSupported() { return false; } /* Non-javadoc * @see java.io.InputStream#mark(int) */ public void mark(int readlimit) { } /* Non-javadoc * @see java.io.InputStream#reset() */ public void reset() throws IOException { throw new IOException(); } /* Non-javadoc * @see java.io.InputStream#skip() */ public long skip(long n) throws IOException { return 0; } /* Non-javadoc * @see java.io.InputStream#close() */ public void close() { isClosed = true; } } /** * native method: construct a DRM content according the mime type. * * @param data input DRM content data to be parsed. * @param len the length of the data. * @param mimeType the mime type of this DRM content. the value of this field includes: * #DRM_MIMETYPE_MESSAGE * #DRM_MIMETYPE_CONTENT * * @return #the id of the DRM content if succeed. * #JNI_DRM_FAILURE if fail. */ private native int nativeConstructDrmContent(InputStream data, int len, int mimeType); /** * native method: get this DRM content rights issuer. * * @return the address of rights issuer if in case of separate delivery. * null if not separete delivery, or otherwise. */ private native String nativeGetRightsAddress(); /** * native method: get this DRM content delivery type. * * @return the delivery method, the value may be one of the following: * #DRM_FORWARD_LOCK * #DRM_COMBINED_DELIVERY * #DRM_SEPARATE_DELIVERY * #DRM_SEPARATE_DELIVERY_DM * #JNI_DRM_FAILURE if fail. */ private native int nativeGetDeliveryMethod(); /** * native method: get a piece of media content data. * * @param buf the buffer to save DRM media content data. * @param bufOff the offset of the buffer to start to save data. * @param len the number of byte to read. * @param mediaOff the offset of the media content data to start to read. * * @return the length of the media content data has been read. * #JNI_DRM_EOF if reach to end of the media content. * #JNI_DRM_FAILURE if fail. */ private native int nativeReadContent(byte[] buf, int bufOff, int len, int mediaOff); /** * native method: get this DRM content type. * * @return the decrypted media content type. * null if fail. */ private native String nativeGetContentType(); /** * native method: get this DRM decrypted media content length. * * @return the length of decrypted media content. * #JNI_DRM_FAILURE if fail. * #JNI_DRM_UNKNOWN_DATA_LEN if the length is unknown currently. */ private native int nativeGetContentLength(); /** * The finalizer of the DRMRawContent. Do some cleanup. */ protected native void finalize(); /** * Load the shared library to link the native methods. */ static { try { System.loadLibrary("drm1_jni"); } catch (UnsatisfiedLinkError ule) { System.err.println("WARNING: Could not load libdrm1_jni.so"); } } }