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:
Jeff Tinker
2013-08-28 17:03:36 -07:00
parent b4dae0af49
commit d4fa39113d
3 changed files with 144 additions and 83 deletions

View File

@@ -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;
} }

View File

@@ -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>

View File

@@ -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;
} }
} }
} }