Improve Widevine MediaDrm plugin tests
1. add missing checks to set the overall result when some tests fail (to verify b/10528466) 2. Fix test result on L1 devices where we can't hash the decrypt result due to inaccessible memory buffers. 3. Configure the codec with a surface to avoid codec errors on L1 devices b/10528466 Merge of https://widevine-internal-review.googlesource.com/#/c/7510/ from the widevine CDM repo Change-Id: I5c7ef5ce802cc4ff63f62524ef2120fb671920f4
This commit is contained in:
@@ -206,17 +206,22 @@ ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE],
|
|||||||
// can then use the hash to verify that decryption was successful.
|
// can then use the hash to verify that decryption was successful.
|
||||||
|
|
||||||
if (mTestMode) {
|
if (mTestMode) {
|
||||||
SHA256_CTX ctx;
|
if (secure) {
|
||||||
uint8_t digest[SHA256_DIGEST_LENGTH];
|
// can't access data in secure mode
|
||||||
SHA256_Init(&ctx);
|
errorDetailMsg->setTo("secure");
|
||||||
SHA256_Update(&ctx, dstPtr, offset);
|
} else {
|
||||||
SHA256_Final(digest, &ctx);
|
SHA256_CTX ctx;
|
||||||
String8 buf;
|
uint8_t digest[SHA256_DIGEST_LENGTH];
|
||||||
for (size_t i = 0; i < sizeof(digest); i++) {
|
SHA256_Init(&ctx);
|
||||||
buf.appendFormat("%02x", digest[i]);
|
SHA256_Update(&ctx, dstPtr, offset);
|
||||||
|
SHA256_Final(digest, &ctx);
|
||||||
|
String8 buf;
|
||||||
|
for (size_t i = 0; i < sizeof(digest); i++) {
|
||||||
|
buf.appendFormat("%02x", digest[i]);
|
||||||
|
}
|
||||||
|
errorDetailMsg->setTo(buf.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
errorDetailMsg->setTo(buf.string());
|
|
||||||
return kErrorTestMode;
|
return kErrorTestMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@android:id/tabhost"
|
android:layout_width="fill_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:layout_height="match_parent">
|
android:orientation="vertical">
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
<com.widevine.test.SurfacePanel
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/surface_view"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="fill_parent"
|
||||||
>
|
android:layout_height="fill_parent"/>
|
||||||
<TabWidget
|
</LinearLayout>
|
||||||
android:id="@android:id/tabs"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@android:id/tabcontent"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
/>
|
|
||||||
</LinearLayout>
|
|
||||||
</TabHost>
|
|
||||||
|
|||||||
@@ -4,9 +4,14 @@
|
|||||||
|
|
||||||
package com.widevine.test;
|
package com.widevine.test;
|
||||||
|
|
||||||
import android.app.TabActivity;
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.content.Context;
|
||||||
import android.media.MediaDrm;
|
import android.media.MediaDrm;
|
||||||
import android.media.MediaDrmException;
|
import android.media.MediaDrmException;
|
||||||
import android.media.NotProvisionedException;
|
import android.media.NotProvisionedException;
|
||||||
@@ -19,6 +24,7 @@ import android.media.MediaCodec.CryptoInfo;
|
|||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.AttributeSet;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@@ -32,7 +38,38 @@ import java.lang.InterruptedException;
|
|||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
public class MediaDrmAPITest extends TabActivity {
|
class SurfacePanel extends SurfaceView implements SurfaceHolder.Callback
|
||||||
|
{
|
||||||
|
private final String TAG = "SurfacePanel";
|
||||||
|
|
||||||
|
public SurfacePanel(Context context, AttributeSet attrSet)
|
||||||
|
{
|
||||||
|
super(context, attrSet);
|
||||||
|
SurfaceHolder holder = getHolder();
|
||||||
|
holder.addCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder)
|
||||||
|
{
|
||||||
|
Log.d(TAG, "surfaceDestroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width,
|
||||||
|
int height)
|
||||||
|
{
|
||||||
|
Log.d(TAG, "surfaceChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder holder)
|
||||||
|
{
|
||||||
|
Log.d(TAG, "surfaceCreated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MediaDrmAPITest extends Activity {
|
||||||
private final String TAG = "MediaDrmAPITest";
|
private final String TAG = "MediaDrmAPITest";
|
||||||
|
|
||||||
static final UUID kWidevineScheme = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
|
static final UUID kWidevineScheme = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
|
||||||
@@ -52,19 +89,24 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.main);
|
setContentView(R.layout.main);
|
||||||
|
|
||||||
mTestFailed = false;
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mTestFailed = false;
|
||||||
|
|
||||||
testWidevineSchemeSupported();
|
testWidevineSchemeSupported();
|
||||||
testProperties();
|
testProperties();
|
||||||
testQueryKeyStatus();
|
testQueryKeyStatus();
|
||||||
testClearContentNoKeys();
|
testClearContentNoKeys();
|
||||||
testEncryptedContent();
|
testEncryptedContent();
|
||||||
|
|
||||||
if (mTestFailed) {
|
if (mTestFailed) {
|
||||||
Log.e(TAG, "TEST FAILED!");
|
Log.e(TAG, "TEST FAILED!");
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "TEST SUCCESS!");
|
Log.e(TAG, "TEST SUCCESS!");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaDrm mDrm;
|
private MediaDrm mDrm;
|
||||||
@@ -88,6 +130,7 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
} catch (MediaDrmException e) {
|
} catch (MediaDrmException e) {
|
||||||
Log.e(TAG, "Failed to create MediaDrm: " + e.getMessage());
|
Log.e(TAG, "Failed to create MediaDrm: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
mTestFailed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +176,7 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
private void stopDrm(MediaDrm drm) {
|
private void stopDrm(MediaDrm drm) {
|
||||||
if (drm != mDrm) {
|
if (drm != mDrm) {
|
||||||
Log.e(TAG, "invalid drm specified in stopDrm");
|
Log.e(TAG, "invalid drm specified in stopDrm");
|
||||||
|
mTestFailed = true;
|
||||||
}
|
}
|
||||||
mLooper.quit();
|
mLooper.quit();
|
||||||
}
|
}
|
||||||
@@ -157,6 +201,7 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
private void testWidevineSchemeSupported() {
|
private void testWidevineSchemeSupported() {
|
||||||
if (!MediaDrm.isCryptoSchemeSupported(kWidevineScheme)) {
|
if (!MediaDrm.isCryptoSchemeSupported(kWidevineScheme)) {
|
||||||
Log.e(TAG, "testWidevineSchemeSupported failed");
|
Log.e(TAG, "testWidevineSchemeSupported failed");
|
||||||
|
mTestFailed = true;
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,6 +289,7 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
} catch (MediaCryptoException e) {
|
} catch (MediaCryptoException e) {
|
||||||
Log.e(TAG, "test failed due to exception: " + e.getMessage());
|
Log.e(TAG, "test failed due to exception: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
mTestFailed = true;
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,9 +301,9 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
codec = MediaCodec.createDecoderByType(mime);
|
codec = MediaCodec.createDecoderByType(mime);
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaFormat format = MediaFormat.createVideoFormat(mime, 640, 480);
|
MediaFormat format = MediaFormat.createVideoFormat(mime, 1280, 720);
|
||||||
codec.configure(format, null, crypto, 0);
|
SurfaceView sv = (SurfaceView)findViewById(R.id.surface_view);
|
||||||
|
codec.configure(format, sv.getHolder().getSurface(), crypto, 0);
|
||||||
codec.start();
|
codec.start();
|
||||||
|
|
||||||
ByteBuffer[] inputBuffers = codec.getInputBuffers();
|
ByteBuffer[] inputBuffers = codec.getInputBuffers();
|
||||||
@@ -311,24 +357,27 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
// in test mode, the WV CryptoPlugin throws a CryptoException where the
|
// in test mode, the WV CryptoPlugin throws a CryptoException where the
|
||||||
// message string contains a SHA256 hash of the decrypted data, for verification
|
// message string contains a SHA256 hash of the decrypted data, for verification
|
||||||
// purposes.
|
// purposes.
|
||||||
MessageDigest digest = null;
|
if (!e.getMessage().equals("secure")) {
|
||||||
try {
|
Log.i(TAG, "e.getMessage()='" + e.getMessage() + "'");
|
||||||
digest = MessageDigest.getInstance("SHA-256");
|
MessageDigest digest = null;
|
||||||
} catch (NoSuchAlgorithmException ex) {
|
try {
|
||||||
ex.printStackTrace();
|
digest = MessageDigest.getInstance("SHA-256");
|
||||||
finish();
|
} catch (NoSuchAlgorithmException ex) {
|
||||||
}
|
ex.printStackTrace();
|
||||||
byte buf[] = Arrays.copyOf(refBuffer.array(), sampleSize);
|
finish();
|
||||||
byte[] sha256 = digest.digest(buf);
|
}
|
||||||
if (Arrays.equals(sha256, hex2ba(e.getMessage()))) {
|
byte buf[] = Arrays.copyOf(refBuffer.array(), sampleSize);
|
||||||
Log.i(TAG, "sha256: " + e.getMessage() + " matches OK");
|
byte[] sha256 = digest.digest(buf);
|
||||||
} else {
|
if (Arrays.equals(sha256, hex2ba(e.getMessage()))) {
|
||||||
Log.i(TAG, "MediaCrypto sha256: " + e.getMessage() +
|
Log.i(TAG, "sha256: " + e.getMessage() + " matches OK");
|
||||||
" does not match test vector sha256: ");
|
} else {
|
||||||
for (int i = 0; i < sha256.length; i++) {
|
Log.i(TAG, "MediaCrypto sha256: " + e.getMessage() +
|
||||||
System.out.printf("%02x", sha256[i]);
|
" does not match test vector sha256: ");
|
||||||
|
for (int i = 0; i < sha256.length; i++) {
|
||||||
|
System.out.printf("%02x", sha256[i]);
|
||||||
|
}
|
||||||
|
mTestFailed = true;
|
||||||
}
|
}
|
||||||
mTestFailed = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,20 +426,23 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
} catch (MediaCryptoException e) {
|
} catch (MediaCryptoException e) {
|
||||||
Log.e(TAG, "test failed due to exception: " + e.getMessage());
|
Log.e(TAG, "test failed due to exception: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
mTestFailed = true;
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
String mime = "video/avc";
|
String mime = "video/avc";
|
||||||
MediaCodec codec;
|
MediaCodec codec;
|
||||||
|
boolean secure = false;
|
||||||
if (crypto.requiresSecureDecoderComponent(mime)) {
|
if (crypto.requiresSecureDecoderComponent(mime)) {
|
||||||
codec = MediaCodec.createByCodecName(getSecureDecoderNameForMime(mime));
|
codec = MediaCodec.createByCodecName(getSecureDecoderNameForMime(mime));
|
||||||
|
secure = true;
|
||||||
} else {
|
} else {
|
||||||
codec = MediaCodec.createDecoderByType(mime);
|
codec = MediaCodec.createDecoderByType(mime);
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaFormat format = MediaFormat.createVideoFormat(mime, 640, 480);
|
MediaFormat format = MediaFormat.createVideoFormat(mime, 1280, 720);
|
||||||
codec.configure(format, null, crypto, 0);
|
SurfaceView sv = (SurfaceView)findViewById(R.id.surface_view);
|
||||||
|
codec.configure(format, sv.getHolder().getSurface(), crypto, 0);
|
||||||
codec.start();
|
codec.start();
|
||||||
|
|
||||||
ByteBuffer[] inputBuffers = codec.getInputBuffers();
|
ByteBuffer[] inputBuffers = codec.getInputBuffers();
|
||||||
@@ -413,8 +465,20 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
inputBuffers[index].put(tv.mClearBuf, 0, tv.mClearBuf.length);
|
inputBuffers[index].put(tv.mClearBuf, 0, tv.mClearBuf.length);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
codec.queueInputBuffer(index, 0 /* offset */, tv.mClearBuf.length,
|
if (secure) {
|
||||||
0 /* sampleTime */, 0 /* flags */);
|
int clearSizes[] = new int[1];
|
||||||
|
clearSizes[0] = tv.mClearBuf.length;
|
||||||
|
int encryptedSizes[] = new int[1];
|
||||||
|
encryptedSizes[0] = 0;
|
||||||
|
|
||||||
|
CryptoInfo info = new CryptoInfo();
|
||||||
|
info.set(1, clearSizes, encryptedSizes, null, null, MediaCodec.CRYPTO_MODE_UNENCRYPTED);
|
||||||
|
codec.queueSecureInputBuffer(index, 0 /* offset */, info,
|
||||||
|
0 /* sampleTime */, 0 /* flags */);
|
||||||
|
} else {
|
||||||
|
codec.queueInputBuffer(index, 0 /* offset */, tv.mClearBuf.length,
|
||||||
|
0 /* sampleTime */, 0 /* flags */);
|
||||||
|
}
|
||||||
} catch (CryptoException e) {
|
} catch (CryptoException e) {
|
||||||
ByteBuffer refBuffer = ByteBuffer.allocate(tv.mClearBuf.length);
|
ByteBuffer refBuffer = ByteBuffer.allocate(tv.mClearBuf.length);
|
||||||
refBuffer.put(tv.mClearBuf, 0, tv.mClearBuf.length);
|
refBuffer.put(tv.mClearBuf, 0, tv.mClearBuf.length);
|
||||||
@@ -422,23 +486,25 @@ public class MediaDrmAPITest extends TabActivity {
|
|||||||
// in test mode, the WV CryptoPlugin throws a CryptoException where the
|
// in test mode, the WV CryptoPlugin throws a CryptoException where the
|
||||||
// message string contains a SHA256 hash of the decrypted data, for verification
|
// message string contains a SHA256 hash of the decrypted data, for verification
|
||||||
// purposes.
|
// purposes.
|
||||||
MessageDigest digest = null;
|
if (!e.getMessage().equals("secure")) {
|
||||||
try {
|
MessageDigest digest = null;
|
||||||
digest = MessageDigest.getInstance("SHA-256");
|
try {
|
||||||
} catch (NoSuchAlgorithmException ex) {
|
digest = MessageDigest.getInstance("SHA-256");
|
||||||
ex.printStackTrace();
|
} catch (NoSuchAlgorithmException ex) {
|
||||||
finish();
|
ex.printStackTrace();
|
||||||
}
|
finish();
|
||||||
byte[] sha256 = digest.digest(refBuffer.array());
|
}
|
||||||
if (Arrays.equals(sha256, hex2ba(e.getMessage()))) {
|
byte[] sha256 = digest.digest(refBuffer.array());
|
||||||
Log.i(TAG, "sha256: " + e.getMessage() + " matches OK");
|
if (Arrays.equals(sha256, hex2ba(e.getMessage()))) {
|
||||||
} else {
|
Log.i(TAG, "sha256: " + e.getMessage() + " matches OK");
|
||||||
Log.i(TAG, "MediaCrypto sha256: " + e.getMessage() +
|
} else {
|
||||||
" does not match test vector sha256: ");
|
Log.i(TAG, "MediaCrypto sha256: " + e.getMessage() +
|
||||||
for (int i = 0; i < sha256.length; i++) {
|
" does not match test vector sha256: ");
|
||||||
System.out.printf("%02x", sha256[i]);
|
for (int i = 0; i < sha256.length; i++) {
|
||||||
|
System.out.printf("%02x", sha256[i]);
|
||||||
|
}
|
||||||
|
mTestFailed = true;
|
||||||
}
|
}
|
||||||
mTestFailed = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user