Commit ce1d00b6 authored by yucliu's avatar yucliu Committed by Commit bot

[Clank] Load/Remove persistent license

Add support for load/remove persistent license at Android level.

Load:
1. Load eme session ID to key set ID map from persistent storage.
2. MediaDrm.restoreKeys.

Remove:
1. Generate key release request.
2. Update session with key release response.
3. Clear persistent storage.

BUG=493521
TEST=test page

Review-Url: https://codereview.chromium.org/2796843002
Cr-Commit-Position: refs/heads/master@{#462690}
parent 9b7ef119
......@@ -18,6 +18,13 @@ std::string JavaBytesToString(JNIEnv* env, jbyteArray j_byte_array) {
return std::string(byte_vector.begin(), byte_vector.end());
}
base::android::ScopedJavaLocalRef<jbyteArray> StringToJavaBytes(
JNIEnv* env,
const std::string& str) {
return base::android::ToJavaByteArray(
env, reinterpret_cast<const uint8_t*>(str.data()), str.size());
}
JavaObjectPtr CreateJavaObjectPtr(jobject object) {
JavaObjectPtr j_object_ptr(new base::android::ScopedJavaGlobalRef<jobject>());
j_object_ptr->Reset(base::android::AttachCurrentThread(), object);
......
......@@ -15,6 +15,11 @@ using JavaObjectPtr =
// Converts jbyteArray (byte[] in Java) into std::string.
std::string JavaBytesToString(JNIEnv* env, jbyteArray j_byte_array);
// Converts std::string to jbyteArray (byte[] in Java).
base::android::ScopedJavaLocalRef<jbyteArray> StringToJavaBytes(
JNIEnv* env,
const std::string& str);
// A helper method to create a JavaObjectPtr.
JavaObjectPtr CreateJavaObjectPtr(jobject object);
......
......@@ -87,6 +87,13 @@ class MediaDrmSessionManager {
return new SessionId(drmId, drmId, null /* keySetId */);
}
/**
* Create session ID used to report session doesn't exist.
*/
static SessionId createNoExistSessionId() {
return createTemporarySessionId(new byte[0]);
}
private SessionId(byte[] emeId, byte[] drmId, byte[] keySetId) {
assert emeId != null;
assert drmId != null || keySetId != null;
......@@ -165,6 +172,16 @@ class MediaDrmSessionManager {
return new PersistentInfo(mSessionId.emeId(), mSessionId.keySetId(), mMimeType);
}
private static SessionInfo fromPersistentInfo(PersistentInfo persistentInfo) {
assert persistentInfo != null;
assert persistentInfo.emeId() != null;
assert persistentInfo.keySetId() != null;
SessionId sessionId = new SessionId(
persistentInfo.emeId(), null /* drmId */, persistentInfo.keySetId());
return new SessionInfo(sessionId, persistentInfo.mimeType(), MediaDrm.KEY_TYPE_OFFLINE);
}
}
// Maps from DRM/EME session ID to SessionInfo. SessionInfo contains
......@@ -188,6 +205,20 @@ class MediaDrmSessionManager {
mStorage = storage;
}
/**
* Set drm ID. It should only be called for persistent license session
* without an opened drm session.
*/
void setDrmId(SessionId sessionId, byte[] drmId) {
SessionInfo info = get(sessionId);
assert info != null;
assert info.sessionId().isEqual(sessionId);
sessionId.setDrmId(drmId);
mDrmSessionInfoMap.put(ByteBuffer.wrap(drmId), info);
}
/**
* Set key set ID. It should only be called for persistent license session.
*/
......@@ -201,6 +232,50 @@ class MediaDrmSessionManager {
mStorage.saveInfo(get(sessionId).toPersistentInfo(), callback);
}
/**
* Mark key as released. It should only be called for persistent license
* session.
*/
void markKeyReleased(SessionId sessionId) {
SessionInfo info = get(sessionId);
assert info != null;
assert info.keyType() == MediaDrm.KEY_TYPE_OFFLINE;
info.setKeyType(MediaDrm.KEY_TYPE_RELEASE);
}
/**
* Load |emeId|'s session data from persistent storage.
*/
void load(byte[] emeId, final Callback<SessionId> callback) {
mStorage.loadInfo(emeId, new Callback<PersistentInfo>() {
@Override
public void onResult(PersistentInfo persistentInfo) {
if (persistentInfo == null) {
callback.onResult(null);
return;
}
// Loading same persistent license into different sessions isn't
// supported.
assert getSessionIdByEmeId(persistentInfo.emeId()) == null;
SessionInfo info = SessionInfo.fromPersistentInfo(persistentInfo);
mEmeSessionInfoMap.put(ByteBuffer.wrap(persistentInfo.emeId()), info);
callback.onResult(info.sessionId());
}
});
}
/**
* Remove persistent license info from persistent storage.
*/
void clearPersistentSessionInfo(SessionId sessionId, Callback<Boolean> callback) {
sessionId.setKeySetId(null);
mStorage.clearInfo(sessionId.emeId(), callback);
}
/**
* Remove session and related infomration from memory, but doesn't touch
* persistent storage.
......
......@@ -42,6 +42,18 @@ class MediaDrmStorageBridge {
mKeySetId = keySetId;
mMimeType = mime;
}
byte[] emeId() {
return mEmeId;
}
byte[] keySetId() {
return mKeySetId;
}
String mimeType() {
return mMimeType;
}
}
MediaDrmStorageBridge(long nativeMediaDrmStorageBridge) {
......
......@@ -124,12 +124,14 @@ ContentDecryptionModule::MessageType GetMessageType(RequestType request_type) {
return ContentDecryptionModule::LICENSE_REQUEST;
}
CdmKeyInformation::KeyStatus ConvertKeyStatus(KeyStatus key_status) {
CdmKeyInformation::KeyStatus ConvertKeyStatus(KeyStatus key_status,
bool is_key_release) {
switch (key_status) {
case KeyStatus::KEY_STATUS_USABLE:
return CdmKeyInformation::USABLE;
case KeyStatus::KEY_STATUS_EXPIRED:
return CdmKeyInformation::EXPIRED;
return is_key_release ? CdmKeyInformation::RELEASED
: CdmKeyInformation::EXPIRED;
case KeyStatus::KEY_STATUS_OUTPUT_NOT_ALLOWED:
return CdmKeyInformation::OUTPUT_RESTRICTED;
case KeyStatus::KEY_STATUS_PENDING:
......@@ -256,6 +258,13 @@ bool AreMediaDrmApisAvailable() {
return true;
}
bool IsPersistentLicenseTypeSupportedByMediaDrm() {
return MediaDrmBridge::IsAvailable() &&
// In development. See http://crbug.com/493521
base::FeatureList::IsEnabled(kMediaDrmPersistentLicense) &&
base::android::BuildInfo::GetInstance()->sdk_int() >= 23;
}
} // namespace
// MediaDrm is not generally usable without MediaCodec. Thus, both the MediaDrm
......@@ -281,15 +290,9 @@ bool MediaDrmBridge::IsKeySystemSupported(const std::string& key_system) {
// static
bool MediaDrmBridge::IsPersistentLicenseTypeSupported(
const std::string& key_system) {
if (!MediaDrmBridge::IsAvailable())
return false;
if (!base::FeatureList::IsEnabled(kMediaDrmPersistentLicense)) {
return false;
}
NOTIMPLEMENTED() << "In development. See http://crbug.com/493521";
return false;
// TODO(yucliu): Check |key_system| if persistent license is supported by
// MediaDrm.
return IsPersistentLicenseTypeSupportedByMediaDrm();
}
// static
......@@ -460,11 +463,20 @@ void MediaDrmBridge::LoadSession(
DCHECK(task_runner_->BelongsToCurrentThread());
DVLOG(2) << __func__;
DCHECK(base::FeatureList::IsEnabled(kMediaDrmPersistentLicense));
DCHECK(IsPersistentLicenseTypeSupportedByMediaDrm());
NOTIMPLEMENTED() << "EME persistent sessions not yet supported on Android.";
promise->reject(CdmPromise::NOT_SUPPORTED_ERROR, 0,
"LoadSession() is not supported.");
if (session_type != CdmSessionType::PERSISTENT_LICENSE_SESSION) {
promise->reject(
CdmPromise::NOT_SUPPORTED_ERROR, 0,
"LoadSession() is only supported for 'persistent-license'.");
return;
}
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> j_session_id =
StringToJavaBytes(env, session_id);
uint32_t promise_id = cdm_promise_adapter_.SavePromise(std::move(promise));
Java_MediaDrmBridge_loadSession(env, j_media_drm_, j_session_id, promise_id);
}
void MediaDrmBridge::UpdateSession(
......@@ -477,9 +489,8 @@ void MediaDrmBridge::UpdateSession(
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> j_response =
base::android::ToJavaByteArray(env, response.data(), response.size());
ScopedJavaLocalRef<jbyteArray> j_session_id = base::android::ToJavaByteArray(
env, reinterpret_cast<const uint8_t*>(session_id.data()),
session_id.size());
ScopedJavaLocalRef<jbyteArray> j_session_id =
StringToJavaBytes(env, session_id);
uint32_t promise_id = cdm_promise_adapter_.SavePromise(std::move(promise));
Java_MediaDrmBridge_updateSession(env, j_media_drm_, j_session_id, j_response,
promise_id);
......@@ -492,9 +503,8 @@ void MediaDrmBridge::CloseSession(
DVLOG(2) << __func__;
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> j_session_id = base::android::ToJavaByteArray(
env, reinterpret_cast<const uint8_t*>(session_id.data()),
session_id.size());
ScopedJavaLocalRef<jbyteArray> j_session_id =
StringToJavaBytes(env, session_id);
uint32_t promise_id = cdm_promise_adapter_.SavePromise(std::move(promise));
Java_MediaDrmBridge_closeSession(env, j_media_drm_, j_session_id, promise_id);
}
......@@ -505,9 +515,12 @@ void MediaDrmBridge::RemoveSession(
DCHECK(task_runner_->BelongsToCurrentThread());
DVLOG(2) << __func__;
NOTIMPLEMENTED() << "EME persistent sessions not yet supported on Android.";
promise->reject(CdmPromise::NOT_SUPPORTED_ERROR, 0,
"RemoveSession() is not supported.");
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> j_session_id =
StringToJavaBytes(env, session_id);
uint32_t promise_id = cdm_promise_adapter_.SavePromise(std::move(promise));
Java_MediaDrmBridge_removeSession(env, j_media_drm_, j_session_id,
promise_id);
}
CdmContext* MediaDrmBridge::GetCdmContext() {
......@@ -698,7 +711,8 @@ void MediaDrmBridge::OnSessionKeysChange(
const JavaParamRef<jobject>& j_media_drm,
const JavaParamRef<jbyteArray>& j_session_id,
const JavaParamRef<jobjectArray>& j_keys_info,
bool has_additional_usable_key) {
bool has_additional_usable_key,
bool is_key_release) {
DVLOG(2) << __func__;
CdmKeysInfo cdm_keys_info;
......@@ -718,7 +732,7 @@ void MediaDrmBridge::OnSessionKeysChange(
jint j_status_code = Java_KeyStatus_getStatusCode(env, j_key_status);
CdmKeyInformation::KeyStatus key_status =
ConvertKeyStatus(static_cast<KeyStatus>(j_status_code));
ConvertKeyStatus(static_cast<KeyStatus>(j_status_code), is_key_release);
DVLOG(2) << __func__ << "Key status change: "
<< base::HexEncode(&key_id[0], key_id.size()) << ", "
......@@ -916,8 +930,7 @@ void MediaDrmBridge::ProcessProvisionResponse(bool success,
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> j_response = base::android::ToJavaByteArray(
env, reinterpret_cast<const uint8_t*>(response.data()), response.size());
ScopedJavaLocalRef<jbyteArray> j_response = StringToJavaBytes(env, response);
Java_MediaDrmBridge_processProvisionResponse(env, j_media_drm_, success,
j_response);
......
......@@ -214,12 +214,17 @@ class MEDIA_EXPORT MediaDrmBridge : public ContentDecryptionModule,
const base::android::JavaParamRef<jobject>& j_media_drm,
const base::android::JavaParamRef<jbyteArray>& j_session_id);
// Called when key statuses of session are changed. |is_key_release| is set to
// true when releasing keys. Some of the MediaDrm key status codes should be
// mapped to CDM key status differently (e.g. EXPIRE -> RELEASED).
void OnSessionKeysChange(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
const base::android::JavaParamRef<jbyteArray>& j_session_id,
// List<KeyStatus>
const base::android::JavaParamRef<jobjectArray>& j_keys_info,
bool has_additional_usable_key);
bool has_additional_usable_key,
bool is_key_release);
// |expiry_time_ms| is the new expiration time for the keys in the session.
// The time is in milliseconds, relative to the Unix epoch. A time of 0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment