1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.media.drmframework.cts; 17 18 import static org.junit.Assert.assertThat; 19 import static org.junit.matchers.JUnitMatchers.containsString; 20 21 import android.content.pm.PackageManager; 22 import android.media.MediaDrm; 23 import android.net.Uri; 24 import android.os.Build; 25 import android.platform.test.annotations.AppModeFull; 26 import android.platform.test.annotations.Presubmit; 27 import android.util.Log; 28 import android.view.Surface; 29 30 import androidx.test.filters.FlakyTest; 31 import androidx.test.filters.SdkSuppress; 32 33 import com.android.compatibility.common.util.ApiLevelUtil; 34 import com.android.compatibility.common.util.MediaUtils; 35 36 import java.io.File; 37 import java.nio.ByteBuffer; 38 import java.util.UUID; 39 40 /** 41 * Tests MediaDrm NDK APIs. ClearKey system uses a subset of NDK APIs, 42 * this test only tests the APIs that are supported by ClearKey system. 43 */ 44 @AppModeFull(reason = "TODO: evaluate and port to instant") 45 public class NativeMediaDrmClearkeyTest extends MediaPlayerDrmTestBase { 46 private static final String TAG = NativeMediaDrmClearkeyTest.class.getSimpleName(); 47 48 private static final int VIDEO_WIDTH_CENC = 1280; 49 private static final int VIDEO_HEIGHT_CENC = 720; 50 private static final String ISO_BMFF_VIDEO_MIME_TYPE = "video/avc"; 51 private static final String MEDIA_DIR = WorkDir.getMediaDirString(); 52 private static final Uri CENC_AUDIO_URL = 53 Uri.fromFile(new File(MEDIA_DIR + "llama_aac_audio.mp4")); 54 private static final Uri CENC_VIDEO_URL = 55 Uri.fromFile(new File(MEDIA_DIR + "llama_h264_main_720p_8000.mp4")); 56 57 private static final int UUID_BYTE_SIZE = 16; 58 private static final UUID COMMON_PSSH_SCHEME_UUID = 59 new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL); 60 private static final UUID CLEARKEY_SCHEME_UUID = 61 new UUID(0xe2719d58a985b3c9L, 0x781ab030af78d30eL); 62 private static final UUID BAD_SCHEME_UUID = 63 new UUID(0xffffffffffffffffL, 0xffffffffffffffffL); 64 65 static { 66 try { 67 System.loadLibrary("mediadrm_jni"); 68 } catch (UnsatisfiedLinkError e) { 69 Log.e(TAG, "NativeMediaDrmClearkeyTest: Error loading JNI library"); 70 e.printStackTrace(); 71 } 72 try { 73 System.loadLibrary("mediandk"); 74 } catch (UnsatisfiedLinkError e) { 75 Log.e(TAG, "NativeMediaDrmClearkeyTest: Error loading JNI library"); 76 e.printStackTrace(); 77 } 78 } 79 80 public static class PlaybackParams { 81 public Surface surface; 82 public String mimeType; 83 public String audioUrl; 84 public String videoUrl; 85 } 86 setUp()87 protected void setUp() throws Exception { 88 super.setUp(); 89 if (false == deviceHasMediaDrm()) { 90 tearDown(); 91 } 92 } 93 tearDown()94 protected void tearDown() throws Exception { 95 super.tearDown(); 96 } 97 watchHasNoClearkeySupport()98 private boolean watchHasNoClearkeySupport() { 99 if (!MediaDrm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) { 100 if (isWatchDevice()) { 101 return true; 102 } else { 103 throw new Error("Crypto scheme is not supported"); 104 } 105 } 106 return false; 107 } 108 isWatchDevice()109 private boolean isWatchDevice() { 110 return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); 111 } 112 deviceHasMediaDrm()113 private boolean deviceHasMediaDrm() { 114 // ClearKey is introduced after KitKat. 115 if (ApiLevelUtil.isAtMost(android.os.Build.VERSION_CODES.KITKAT)) { 116 return false; 117 } 118 return true; 119 } 120 uuidByteArray(UUID uuid)121 private static final byte[] uuidByteArray(UUID uuid) { 122 ByteBuffer buffer = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]); 123 buffer.putLong(uuid.getMostSignificantBits()); 124 buffer.putLong(uuid.getLeastSignificantBits()); 125 return buffer.array(); 126 } 127 128 @Presubmit testIsCryptoSchemeSupported()129 public void testIsCryptoSchemeSupported() throws Exception { 130 if (watchHasNoClearkeySupport()) { 131 return; 132 } 133 134 assertTrue(isCryptoSchemeSupportedNative(uuidByteArray(COMMON_PSSH_SCHEME_UUID))); 135 assertTrue(isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID))); 136 } 137 138 @Presubmit testIsCryptoSchemeNotSupported()139 public void testIsCryptoSchemeNotSupported() throws Exception { 140 assertFalse(isCryptoSchemeSupportedNative(uuidByteArray(BAD_SCHEME_UUID))); 141 } 142 143 @Presubmit testPssh()144 public void testPssh() throws Exception { 145 // The test uses a canned PSSH that contains the common box UUID. 146 assertTrue(testPsshNative(uuidByteArray(COMMON_PSSH_SCHEME_UUID), 147 CENC_VIDEO_URL.toString())); 148 } 149 150 @Presubmit testQueryKeyStatus()151 public void testQueryKeyStatus() throws Exception { 152 if (watchHasNoClearkeySupport()) { 153 return; 154 } 155 156 assertTrue(testQueryKeyStatusNative(uuidByteArray(CLEARKEY_SCHEME_UUID))); 157 } 158 159 @Presubmit testFindSessionId()160 public void testFindSessionId() throws Exception { 161 if (watchHasNoClearkeySupport()) { 162 return; 163 } 164 165 assertTrue(testFindSessionIdNative(uuidByteArray(CLEARKEY_SCHEME_UUID))); 166 } 167 168 @Presubmit testGetPropertyString()169 public void testGetPropertyString() throws Exception { 170 if (watchHasNoClearkeySupport()) { 171 return; 172 } 173 174 StringBuffer value = new StringBuffer(); 175 testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value); 176 assertEquals("ClearKey CDM", value.toString()); 177 178 value.delete(0, value.length()); 179 testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value); 180 assertEquals("ClearKey CDM", value.toString()); 181 } 182 183 @Presubmit testPropertyByteArray()184 public void testPropertyByteArray() throws Exception { 185 if (watchHasNoClearkeySupport()) { 186 return; 187 } 188 189 assertTrue(testPropertyByteArrayNative(uuidByteArray(CLEARKEY_SCHEME_UUID))); 190 } 191 192 @Presubmit testUnknownPropertyString()193 public void testUnknownPropertyString() throws Exception { 194 StringBuffer value = new StringBuffer(); 195 196 try { 197 testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), 198 "unknown-property", value); 199 fail("Should have thrown an exception"); 200 } catch (RuntimeException e) { 201 Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'"); 202 assertThat(e.getMessage(), containsString("get property string returns")); 203 } 204 205 value.delete(0, value.length()); 206 try { 207 testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), 208 "unknown-property", value); 209 fail("Should have thrown an exception"); 210 } catch (RuntimeException e) { 211 Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'"); 212 assertThat(e.getMessage(), containsString("get property string returns")); 213 } 214 } 215 216 /** 217 * Tests native clear key system playback. 218 */ testClearKeyPlayback( UUID drmSchemeUuid, String mimeType, Uri audioUrl, Uri videoUrl, int videoWidth, int videoHeight)219 private void testClearKeyPlayback( 220 UUID drmSchemeUuid, String mimeType, /*String initDataType,*/ Uri audioUrl, Uri videoUrl, 221 int videoWidth, int videoHeight) throws Exception { 222 223 if (isWatchDevice()) { 224 return; 225 } 226 227 if (!isCryptoSchemeSupportedNative(uuidByteArray(drmSchemeUuid))) { 228 throw new Error("Crypto scheme is not supported."); 229 } 230 231 if (!MediaUtils.checkCodecsForPath(mContext, videoUrl.toString())) { 232 Log.i(TAG, "Device does not support " + 233 videoWidth + "x" + videoHeight + " resolution for " + mimeType); 234 return; // skip 235 } 236 237 PlaybackParams params = new PlaybackParams(); 238 params.surface = mActivity.getSurfaceHolder().getSurface(); 239 params.mimeType = mimeType; 240 params.audioUrl = audioUrl.toString(); 241 params.videoUrl = videoUrl.toString(); 242 243 if (!testClearKeyPlaybackNative( 244 uuidByteArray(drmSchemeUuid), params)) { 245 Log.e(TAG, "Fails play back using native media drm APIs."); 246 } 247 params.surface.release(); 248 } 249 isCryptoSchemeSupportedNative(final byte[] uuid)250 private static native boolean isCryptoSchemeSupportedNative(final byte[] uuid); 251 testClearKeyPlaybackNative(final byte[] uuid, PlaybackParams params)252 private static native boolean testClearKeyPlaybackNative(final byte[] uuid, 253 PlaybackParams params); 254 testFindSessionIdNative(final byte[] uuid)255 private static native boolean testFindSessionIdNative(final byte[] uuid); 256 testGetPropertyStringNative(final byte[] uuid, final String name, StringBuffer value)257 private static native boolean testGetPropertyStringNative(final byte[] uuid, 258 final String name, StringBuffer value); 259 testPropertyByteArrayNative(final byte[] uuid)260 private static native boolean testPropertyByteArrayNative(final byte[] uuid); 261 testPsshNative(final byte[] uuid, final String videoUrl)262 private static native boolean testPsshNative(final byte[] uuid, final String videoUrl); 263 testQueryKeyStatusNative(final byte[] uuid)264 private static native boolean testQueryKeyStatusNative(final byte[] uuid); 265 testGetKeyRequestNative(final byte[] uuid, PlaybackParams params)266 private static native boolean testGetKeyRequestNative(final byte[] uuid, 267 PlaybackParams params); 268 testClearKeyPlaybackCenc()269 public void testClearKeyPlaybackCenc() throws Exception { 270 testClearKeyPlayback( 271 COMMON_PSSH_SCHEME_UUID, 272 ISO_BMFF_VIDEO_MIME_TYPE, 273 CENC_AUDIO_URL, 274 CENC_VIDEO_URL, 275 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC); 276 } 277 278 @FlakyTest(bugId = 173646795) 279 @Presubmit testClearKeyPlaybackCenc2()280 public void testClearKeyPlaybackCenc2() throws Exception { 281 testClearKeyPlayback( 282 CLEARKEY_SCHEME_UUID, 283 ISO_BMFF_VIDEO_MIME_TYPE, 284 CENC_AUDIO_URL, 285 CENC_VIDEO_URL, 286 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC); 287 } 288 289 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU) testClearKeyGetKeyRequest()290 public void testClearKeyGetKeyRequest() throws Exception { 291 PlaybackParams params = new PlaybackParams(); 292 params.surface = mActivity.getSurfaceHolder().getSurface(); 293 params.mimeType = ISO_BMFF_VIDEO_MIME_TYPE; 294 params.audioUrl = CENC_AUDIO_URL.toString(); 295 params.videoUrl = CENC_VIDEO_URL.toString(); 296 boolean status = testGetKeyRequestNative( 297 uuidByteArray(CLEARKEY_SCHEME_UUID), 298 params); 299 assertTrue(status); 300 params.surface.release(); 301 } 302 } 303 304