465 lines
13 KiB
Java
465 lines
13 KiB
Java
|
/*
|
||
|
* 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");
|
||
|
}
|
||
|
}
|
||
|
}
|