/*
* 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");
}
}
}