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.cts; 17 18 import android.content.pm.PackageManager; 19 import android.media.MediaDrm; 20 import android.net.Uri; 21 import android.platform.test.annotations.AppModeFull; 22 import android.util.Log; 23 import android.view.Surface; 24 25 import com.android.compatibility.common.util.ApiLevelUtil; 26 import com.android.compatibility.common.util.MediaUtils; 27 import com.google.android.collect.Lists; 28 29 import java.nio.ByteBuffer; 30 import java.util.ArrayList; 31 import java.util.UUID; 32 33 import static org.junit.Assert.assertThat; 34 import static org.junit.matchers.JUnitMatchers.containsString; 35 36 /** 37 * Tests MediaDrm NDK APIs. ClearKey system uses a subset of NDK APIs, 38 * this test only tests the APIs that are supported by ClearKey system. 39 */ 40 @AppModeFull(reason = "TODO: evaluate and port to instant") 41 public class NativeMediaDrmClearkeyTest extends MediaPlayerTestBase { 42 private static final String TAG = NativeMediaDrmClearkeyTest.class.getSimpleName(); 43 44 private static final int CONNECTION_RETRIES = 10; 45 private static final int VIDEO_WIDTH_CENC = 1280; 46 private static final int VIDEO_HEIGHT_CENC = 720; 47 private static final String ISO_BMFF_VIDEO_MIME_TYPE = "video/avc"; 48 private static final String ISO_BMFF_AUDIO_MIME_TYPE = "audio/avc"; 49 private static final String CENC_AUDIO_PATH = 50 "/clear/h264/llama/llama_aac_audio.mp4"; 51 52 private static final String CENC_CLEARKEY_VIDEO_PATH = 53 "/clearkey/llama_h264_main_720p_8000.mp4"; 54 55 private static final int UUID_BYTE_SIZE = 16; 56 private static final UUID COMMON_PSSH_SCHEME_UUID = 57 new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL); 58 private static final UUID CLEARKEY_SCHEME_UUID = 59 new UUID(0xe2719d58a985b3c9L, 0x781ab030af78d30eL); 60 private static final UUID BAD_SCHEME_UUID = 61 new UUID(0xffffffffffffffffL, 0xffffffffffffffffL); 62 private MediaCodecClearKeyPlayer mMediaCodecPlayer; 63 64 static { 65 try { 66 System.loadLibrary("ctsmediadrm_jni"); 67 } catch (UnsatisfiedLinkError e) { 68 Log.e(TAG, "NativeMediaDrmClearkeyTest: Error loading JNI library"); 69 e.printStackTrace(); 70 } 71 try { 72 System.loadLibrary("mediandk"); 73 } catch (UnsatisfiedLinkError e) { 74 Log.e(TAG, "NativeMediaDrmClearkeyTest: Error loading JNI library"); 75 e.printStackTrace(); 76 } 77 } 78 79 public static class PlaybackParams { 80 public Surface surface; 81 public String mimeType; 82 public String audioUrl; 83 public String videoUrl; 84 } 85 86 @Override setUp()87 protected void setUp() throws Exception { 88 super.setUp(); 89 if (false == deviceHasMediaDrm()) { 90 tearDown(); 91 } 92 } 93 94 @Override tearDown()95 protected void tearDown() throws Exception { 96 super.tearDown(); 97 } 98 watchHasNoClearkeySupport()99 private boolean watchHasNoClearkeySupport() { 100 if (!MediaDrm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) { 101 if (isWatchDevice()) { 102 return true; 103 } else { 104 throw new Error("Crypto scheme is not supported"); 105 } 106 } 107 return false; 108 } 109 isWatchDevice()110 private boolean isWatchDevice() { 111 return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); 112 } 113 deviceHasMediaDrm()114 private boolean deviceHasMediaDrm() { 115 // ClearKey is introduced after KitKat. 116 if (ApiLevelUtil.isAtMost(android.os.Build.VERSION_CODES.KITKAT)) { 117 return false; 118 } 119 return true; 120 } 121 uuidByteArray(UUID uuid)122 private static final byte[] uuidByteArray(UUID uuid) { 123 ByteBuffer buffer = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]); 124 buffer.putLong(uuid.getMostSignificantBits()); 125 buffer.putLong(uuid.getLeastSignificantBits()); 126 return buffer.array(); 127 } 128 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 testIsCryptoSchemeNotSupported()138 public void testIsCryptoSchemeNotSupported() throws Exception { 139 assertFalse(isCryptoSchemeSupportedNative(uuidByteArray(BAD_SCHEME_UUID))); 140 } 141 testPssh()142 public void testPssh() throws Exception { 143 // The test uses a canned PSSH that contains the common box UUID. 144 assertTrue(testPsshNative(uuidByteArray(COMMON_PSSH_SCHEME_UUID), 145 Uri.parse(Utils.getMediaPath() + CENC_CLEARKEY_VIDEO_PATH).toString())); 146 } 147 testQueryKeyStatus()148 public void testQueryKeyStatus() throws Exception { 149 if (watchHasNoClearkeySupport()) { 150 return; 151 } 152 153 assertTrue(testQueryKeyStatusNative(uuidByteArray(CLEARKEY_SCHEME_UUID))); 154 } 155 testFindSessionId()156 public void testFindSessionId() throws Exception { 157 if (watchHasNoClearkeySupport()) { 158 return; 159 } 160 161 assertTrue(testFindSessionIdNative(uuidByteArray(CLEARKEY_SCHEME_UUID))); 162 } 163 testGetPropertyString()164 public void testGetPropertyString() throws Exception { 165 if (watchHasNoClearkeySupport()) { 166 return; 167 } 168 169 StringBuffer value = new StringBuffer(); 170 testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value); 171 assertEquals("ClearKey CDM", value.toString()); 172 173 value.delete(0, value.length()); 174 testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value); 175 assertEquals("ClearKey CDM", value.toString()); 176 } 177 testPropertyByteArray()178 public void testPropertyByteArray() throws Exception { 179 if (watchHasNoClearkeySupport()) { 180 return; 181 } 182 183 assertTrue(testPropertyByteArrayNative(uuidByteArray(CLEARKEY_SCHEME_UUID))); 184 } 185 testUnknownPropertyString()186 public void testUnknownPropertyString() throws Exception { 187 StringBuffer value = new StringBuffer(); 188 189 try { 190 testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), 191 "unknown-property", value); 192 fail("Should have thrown an exception"); 193 } catch (RuntimeException e) { 194 Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'"); 195 assertThat(e.getMessage(), containsString("get property string returns")); 196 } 197 198 value.delete(0, value.length()); 199 try { 200 testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), 201 "unknown-property", value); 202 fail("Should have thrown an exception"); 203 } catch (RuntimeException e) { 204 Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'"); 205 assertThat(e.getMessage(), containsString("get property string returns")); 206 } 207 } 208 209 /** 210 * Tests native clear key system playback. 211 */ testClearKeyPlayback( UUID drmSchemeUuid, String mimeType, Uri audioUrl, Uri videoUrl, int videoWidth, int videoHeight)212 private void testClearKeyPlayback( 213 UUID drmSchemeUuid, String mimeType, /*String initDataType,*/ Uri audioUrl, Uri videoUrl, 214 int videoWidth, int videoHeight) throws Exception { 215 216 if (isWatchDevice()) { 217 return; 218 } 219 220 if (!isCryptoSchemeSupportedNative(uuidByteArray(drmSchemeUuid))) { 221 throw new Error("Crypto scheme is not supported."); 222 } 223 224 IConnectionStatus connectionStatus = new ConnectionStatus(mContext); 225 if (!connectionStatus.isAvailable()) { 226 throw new Error("Network is not available, reason: " + 227 connectionStatus.getNotConnectedReason()); 228 } 229 230 // If device is not online, recheck the status a few times. 231 int retries = 0; 232 while (!connectionStatus.isConnected()) { 233 if (retries++ >= CONNECTION_RETRIES) { 234 throw new Error("Device is not online, reason: " + 235 connectionStatus.getNotConnectedReason()); 236 } 237 try { 238 Thread.sleep(100); 239 } catch (InterruptedException e) { 240 // do nothing 241 } 242 } 243 connectionStatus.testConnection(videoUrl); 244 245 if (!MediaUtils.checkCodecsForPath(mContext, videoUrl.toString())) { 246 Log.i(TAG, "Device does not support " + 247 videoWidth + "x" + videoHeight + " resolution for " + mimeType); 248 return; // skip 249 } 250 251 PlaybackParams params = new PlaybackParams(); 252 params.surface = mActivity.getSurfaceHolder().getSurface(); 253 params.mimeType = mimeType; 254 params.audioUrl = audioUrl.toString(); 255 params.videoUrl = videoUrl.toString(); 256 257 if (!testClearKeyPlaybackNative( 258 uuidByteArray(drmSchemeUuid), params)) { 259 Log.e(TAG, "Fails play back using native media drm APIs."); 260 } 261 params.surface.release(); 262 } 263 intVersion(String version)264 private ArrayList<Integer> intVersion(String version) { 265 String versions[] = version.split("\\."); 266 267 ArrayList<Integer> versionNumbers = Lists.newArrayList(); 268 for (String subVersion : versions) { 269 versionNumbers.add(Integer.parseInt(subVersion)); 270 } 271 return versionNumbers; 272 } 273 isCryptoSchemeSupportedNative(final byte[] uuid)274 private static native boolean isCryptoSchemeSupportedNative(final byte[] uuid); 275 testClearKeyPlaybackNative(final byte[] uuid, PlaybackParams params)276 private static native boolean testClearKeyPlaybackNative(final byte[] uuid, 277 PlaybackParams params); 278 testFindSessionIdNative(final byte[] uuid)279 private static native boolean testFindSessionIdNative(final byte[] uuid); 280 testGetPropertyStringNative(final byte[] uuid, final String name, StringBuffer value)281 private static native boolean testGetPropertyStringNative(final byte[] uuid, 282 final String name, StringBuffer value); 283 testPropertyByteArrayNative(final byte[] uuid)284 private static native boolean testPropertyByteArrayNative(final byte[] uuid); 285 testPsshNative(final byte[] uuid, final String videoUrl)286 private static native boolean testPsshNative(final byte[] uuid, final String videoUrl); 287 testQueryKeyStatusNative(final byte[] uuid)288 private static native boolean testQueryKeyStatusNative(final byte[] uuid); 289 testClearKeyPlaybackCenc()290 public void testClearKeyPlaybackCenc() throws Exception { 291 testClearKeyPlayback( 292 COMMON_PSSH_SCHEME_UUID, 293 ISO_BMFF_VIDEO_MIME_TYPE, 294 Uri.parse(Utils.getMediaPath() + CENC_AUDIO_PATH), 295 Uri.parse(Utils.getMediaPath() + CENC_CLEARKEY_VIDEO_PATH), 296 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC); 297 } 298 testClearKeyPlaybackCenc2()299 public void testClearKeyPlaybackCenc2() throws Exception { 300 testClearKeyPlayback( 301 CLEARKEY_SCHEME_UUID, 302 ISO_BMFF_VIDEO_MIME_TYPE, 303 Uri.parse(Utils.getMediaPath() + CENC_AUDIO_PATH), 304 Uri.parse(Utils.getMediaPath() + CENC_CLEARKEY_VIDEO_PATH), 305 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC); 306 } 307 } 308 309