1 /* 2 * Copyright (C) 2014 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 android.media.MediaCodecInfo.CodecCapabilities; 19 import android.media.MediaDrm; 20 import android.media.MediaDrm.KeyStatus; 21 import android.media.MediaDrm.MediaDrmStateException; 22 import android.media.MediaDrmException; 23 import android.media.MediaDrmThrowable; 24 import android.media.MediaFormat; 25 import android.media.NotProvisionedException; 26 import android.media.ResourceBusyException; 27 import android.media.UnsupportedSchemeException; 28 import android.media.cts.MediaCodecClearKeyPlayer; 29 import android.media.cts.MediaCodecPlayerTestBase; 30 import android.media.cts.MediaStubActivity; 31 import android.media.cts.TestUtils.Monitor; 32 import android.net.Uri; 33 import android.os.Build; 34 import android.os.Looper; 35 import android.os.SystemProperties; 36 import android.platform.test.annotations.AppModeFull; 37 import android.platform.test.annotations.Presubmit; 38 import android.util.Base64; 39 import android.util.Log; 40 import android.view.Surface; 41 42 import androidx.annotation.NonNull; 43 import androidx.test.filters.SdkSuppress; 44 45 import com.android.compatibility.common.util.ApiLevelUtil; 46 import com.android.compatibility.common.util.ApiTest; 47 import com.android.compatibility.common.util.FrameworkSpecificTest; 48 import com.android.compatibility.common.util.ModuleSpecificTest; 49 50 import org.json.JSONArray; 51 import org.json.JSONException; 52 import org.json.JSONObject; 53 import org.junit.Assert; 54 55 import java.io.File; 56 import java.nio.charset.Charset; 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.HashMap; 60 import java.util.List; 61 import java.util.Set; 62 import java.util.UUID; 63 import java.util.Vector; 64 65 /** 66 * Tests of MediaPlayer streaming capabilities. 67 */ 68 @AppModeFull(reason = "Instant apps cannot access the SD card") 69 public class MediaDrmClearkeyTest extends MediaCodecPlayerTestBase<MediaStubActivity> { 70 71 private static final String TAG = MediaDrmClearkeyTest.class.getSimpleName(); 72 73 // Add additional keys here if the content has more keys. 74 private static final byte[] CLEAR_KEY_CENC = { 75 (byte)0x3f, (byte)0x0a, (byte)0x33, (byte)0xf3, (byte)0x40, (byte)0x98, (byte)0xb9, (byte)0xe2, 76 (byte)0x2b, (byte)0xc0, (byte)0x78, (byte)0xe0, (byte)0xa1, (byte)0xb5, (byte)0xe8, (byte)0x54 }; 77 78 private static final byte[] CLEAR_KEY_WEBM = "_CLEAR_KEY_WEBM_".getBytes(); 79 80 private static final int NUMBER_OF_SECURE_STOPS = 10; 81 private static final int VIDEO_WIDTH_CENC = 1280; 82 private static final int VIDEO_HEIGHT_CENC = 720; 83 private static final int VIDEO_WIDTH_WEBM = 352; 84 private static final int VIDEO_HEIGHT_WEBM = 288; 85 private static final int VIDEO_WIDTH_MPEG2TS = 320; 86 private static final int VIDEO_HEIGHT_MPEG2TS = 240; 87 private static final String MIME_VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC; 88 private static final String MIME_VIDEO_VP8 = MediaFormat.MIMETYPE_VIDEO_VP8; 89 90 // Property Keys 91 private static final String ALGORITHMS_PROPERTY_KEY = MediaDrm.PROPERTY_ALGORITHMS; 92 private static final String DESCRIPTION_PROPERTY_KEY = MediaDrm.PROPERTY_DESCRIPTION; 93 private static final String DEVICEID_PROPERTY_KEY = "deviceId"; 94 private static final String INVALID_PROPERTY_KEY = "invalid property key"; 95 private static final String LISTENER_TEST_SUPPORT_PROPERTY_KEY = "listenerTestSupport"; 96 private static final String VENDOR_PROPERTY_KEY = MediaDrm.PROPERTY_VENDOR; 97 private static final String VERSION_PROPERTY_KEY = MediaDrm.PROPERTY_VERSION; 98 99 // Error message 100 private static final String ERR_MSG_CRYPTO_SCHEME_NOT_SUPPORTED = "Crypto scheme is not supported"; 101 private static final String MEDIA_DIR = WorkDir.getMediaDirString(); 102 private static final Uri CENC_AUDIO_URL = 103 Uri.fromFile(new File(MEDIA_DIR + "llama_aac_audio.mp4")); 104 private static final Uri CENC_VIDEO_URL = 105 Uri.fromFile(new File(MEDIA_DIR + "llama_h264_main_720p_8000.mp4")); 106 private static final Uri WEBM_URL = 107 Uri.fromFile(new File(MEDIA_DIR 108 + "video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt" 109 + ".webm")); 110 private static final Uri MPEG2TS_SCRAMBLED_URL = 111 Uri.fromFile(new File(MEDIA_DIR + "segment000001_scrambled.ts")); 112 private static final Uri MPEG2TS_CLEAR_URL = 113 Uri.fromFile(new File(MEDIA_DIR + "segment000001.ts")); 114 115 private static final UUID COMMON_PSSH_SCHEME_UUID = 116 new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL); 117 private static final UUID CLEARKEY_SCHEME_UUID = 118 new UUID(0xe2719d58a985b3c9L, 0x781ab030af78d30eL); 119 120 private byte[] mDrmInitData; 121 private byte[] mKeySetId; 122 private byte[] mSessionId; 123 private Monitor mSessionMonitor = new Monitor(); 124 private Looper mLooper; 125 private MediaDrm mDrm = null; 126 private final Object mLock = new Object(); 127 private boolean mEventListenerCalled; 128 private boolean mExpirationUpdateReceived; 129 private boolean mLostStateReceived; 130 131 private static boolean sIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S); 132 private static final boolean VNDK_IS_AT_LEAST_U = 133 SystemProperties.getInt("ro.vndk.version", Build.VERSION_CODES.CUR_DEVELOPMENT) 134 >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 135 136 private static final boolean FIRST_RELEASE_IS_AT_LEAST_U = 137 SystemProperties.getInt("ro.product.first_api_level", Build.VERSION_CODES.CUR_DEVELOPMENT) 138 >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 139 MediaDrmClearkeyTest()140 public MediaDrmClearkeyTest() { 141 super(MediaStubActivity.class); 142 } 143 144 @Override setUp()145 protected void setUp() throws Exception { 146 super.setUp(); 147 if (false == deviceHasMediaDrm()) { 148 tearDown(); 149 } 150 } 151 152 @Override tearDown()153 protected void tearDown() throws Exception { 154 super.tearDown(); 155 } 156 deviceHasMediaDrm()157 private boolean deviceHasMediaDrm() { 158 // ClearKey is introduced after KitKat. 159 if (ApiLevelUtil.isAtMost(android.os.Build.VERSION_CODES.KITKAT)) { 160 return false; 161 } 162 return true; 163 } 164 165 /** 166 * Extracts key ids from the pssh blob returned by getKeyRequest() and 167 * places it in keyIds. 168 * keyRequestBlob format (section 5.1.3.1): 169 * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#clear-key 170 * 171 * @return size of keyIds vector that contains the key ids, 0 for error 172 */ getKeyIds(byte[] keyRequestBlob, Vector<String> keyIds)173 private static int getKeyIds(byte[] keyRequestBlob, Vector<String> keyIds) { 174 if (0 == keyRequestBlob.length || keyIds == null) 175 return 0; 176 177 String jsonLicenseRequest = new String(keyRequestBlob); 178 keyIds.clear(); 179 180 try { 181 JSONObject license = new JSONObject(jsonLicenseRequest); 182 final JSONArray ids = license.getJSONArray("kids"); 183 for (int i = 0; i < ids.length(); ++i) { 184 keyIds.add(ids.getString(i)); 185 } 186 } catch (JSONException e) { 187 Log.e(TAG, "Invalid JSON license = " + jsonLicenseRequest); 188 return 0; 189 } 190 return keyIds.size(); 191 } 192 193 /** 194 * Creates the JSON Web Key string. 195 * 196 * @return JSON Web Key string. 197 */ createJsonWebKeySet( Vector<String> keyIds, Vector<String> keys, int keyType)198 private static String createJsonWebKeySet( 199 Vector<String> keyIds, Vector<String> keys, int keyType) { 200 String jwkSet = "{\"keys\":["; 201 for (int i = 0; i < keyIds.size(); ++i) { 202 String id = new String(keyIds.get(i).getBytes(Charset.forName("UTF-8"))); 203 String key = new String(keys.get(i).getBytes(Charset.forName("UTF-8"))); 204 205 jwkSet += "{\"kty\":\"oct\",\"kid\":\"" + id + 206 "\",\"k\":\"" + key + "\"}"; 207 } 208 jwkSet += "], \"type\":"; 209 if (keyType == MediaDrm.KEY_TYPE_OFFLINE || keyType == MediaDrm.KEY_TYPE_RELEASE) { 210 jwkSet += "\"persistent-license\" }"; 211 } else { 212 jwkSet += "\"temporary\" }"; 213 } 214 return jwkSet; 215 } 216 217 /** 218 * Retrieves clear key ids from getKeyRequest(), create JSON Web Key 219 * set and send it to the CDM via provideKeyResponse(). 220 * 221 * @return key set ID 222 */ retrieveKeys(MediaDrm drm, String initDataType, byte[] sessionId, byte[] drmInitData, int keyType, byte[][] clearKeyIds)223 public static byte[] retrieveKeys(MediaDrm drm, String initDataType, 224 byte[] sessionId, byte[] drmInitData, int keyType, byte[][] clearKeyIds) { 225 MediaDrm.KeyRequest drmRequest = null; 226 try { 227 drmRequest = drm.getKeyRequest(sessionId, drmInitData, initDataType, 228 keyType, null); 229 } catch (Exception e) { 230 e.printStackTrace(); 231 Log.i(TAG, "Failed to get key request: " + e.toString()); 232 } 233 if (drmRequest == null) { 234 Log.e(TAG, "Failed getKeyRequest"); 235 return null; 236 } 237 238 Vector<String> keyIds = new Vector<String>(); 239 if (0 == getKeyIds(drmRequest.getData(), keyIds)) { 240 Log.e(TAG, "No key ids found in initData"); 241 return null; 242 } 243 244 if (clearKeyIds.length != keyIds.size()) { 245 Log.e(TAG, "Mismatch number of key ids and keys: ids=" + 246 keyIds.size() + ", keys=" + clearKeyIds.length); 247 return null; 248 } 249 250 // Base64 encodes clearkeys. Keys are known to the application. 251 Vector<String> keys = new Vector<String>(); 252 for (int i = 0; i < clearKeyIds.length; ++i) { 253 String clearKey = Base64.encodeToString(clearKeyIds[i], 254 Base64.NO_PADDING | Base64.NO_WRAP); 255 keys.add(clearKey); 256 } 257 258 String jwkSet = createJsonWebKeySet(keyIds, keys, keyType); 259 byte[] jsonResponse = jwkSet.getBytes(Charset.forName("UTF-8")); 260 261 try { 262 try { 263 return drm.provideKeyResponse(sessionId, jsonResponse); 264 } catch (IllegalStateException e) { 265 Log.e(TAG, "Failed to provide key response: " + e.toString()); 266 } 267 } catch (Exception e) { 268 e.printStackTrace(); 269 Log.e(TAG, "Failed to provide key response: " + e.toString()); 270 } 271 return null; 272 } 273 274 /** 275 * Retrieves clear key ids from getKeyRequest(), create JSON Web Key 276 * set and send it to the CDM via provideKeyResponse(). 277 */ getKeys(MediaDrm drm, String initDataType, byte[] sessionId, byte[] drmInitData, int keyType, byte[][] clearKeyIds)278 private void getKeys(MediaDrm drm, String initDataType, 279 byte[] sessionId, byte[] drmInitData, int keyType, byte[][] clearKeyIds) { 280 mKeySetId = retrieveKeys(drm, initDataType, sessionId, drmInitData, keyType, clearKeyIds); 281 } 282 startDrm(final byte[][] clearKeyIds, final String initDataType, final UUID drmSchemeUuid, int keyType)283 private @NonNull MediaDrm startDrm(final byte[][] clearKeyIds, final String initDataType, 284 final UUID drmSchemeUuid, int keyType) { 285 if (!MediaDrm.isCryptoSchemeSupported(drmSchemeUuid)) { 286 throw new Error(ERR_MSG_CRYPTO_SCHEME_NOT_SUPPORTED); 287 } 288 289 new Thread() { 290 @Override 291 public void run() { 292 if (mDrm != null) { 293 Log.e(TAG, "Failed to startDrm: already started"); 294 return; 295 } 296 // Set up a looper to handle events 297 Looper.prepare(); 298 299 // Save the looper so that we can terminate this thread 300 // after we are done with it. 301 mLooper = Looper.myLooper(); 302 303 try { 304 mDrm = new MediaDrm(drmSchemeUuid); 305 } catch (MediaDrmException e) { 306 Log.e(TAG, "Failed to create MediaDrm: " + e.getMessage()); 307 return; 308 } 309 310 synchronized(mLock) { 311 mDrm.setOnEventListener(new MediaDrm.OnEventListener() { 312 @Override 313 public void onEvent(MediaDrm md, byte[] sid, int event, 314 int extra, byte[] data) { 315 if (md != mDrm) { 316 Log.e(TAG, "onEvent callback: drm object mismatch"); 317 return; 318 } else if (!Arrays.equals(mSessionId, sid)) { 319 Log.e(TAG, "onEvent callback: sessionId mismatch: |" + 320 Arrays.toString(mSessionId) + "| vs |" + Arrays.toString(sid) + "|"); 321 return; 322 } 323 324 mEventListenerCalled = true; 325 if (event == MediaDrm.EVENT_PROVISION_REQUIRED) { 326 Log.i(TAG, "MediaDrm event: Provision required"); 327 } else if (event == MediaDrm.EVENT_KEY_REQUIRED) { 328 Log.i(TAG, "MediaDrm event: Key required"); 329 getKeys(mDrm, initDataType, mSessionId, mDrmInitData, 330 keyType, clearKeyIds); 331 } else if (event == MediaDrm.EVENT_KEY_EXPIRED) { 332 Log.i(TAG, "MediaDrm event: Key expired"); 333 getKeys(mDrm, initDataType, mSessionId, mDrmInitData, 334 keyType, clearKeyIds); 335 } else if (event == MediaDrm.EVENT_VENDOR_DEFINED) { 336 Log.i(TAG, "MediaDrm event: Vendor defined"); 337 } else if (event == MediaDrm.EVENT_SESSION_RECLAIMED) { 338 Log.i(TAG, "MediaDrm event: Session reclaimed"); 339 } else { 340 Log.e(TAG, "MediaDrm event not supported: " + event); 341 } 342 } 343 }); 344 mDrm.setOnExpirationUpdateListener(new MediaDrm.OnExpirationUpdateListener() { 345 @Override 346 public void onExpirationUpdate(MediaDrm md, byte[] sid, long expirationTime) { 347 if (md != mDrm) { 348 Log.e(TAG, "onExpirationUpdate callback: drm object mismatch"); 349 } else if (!Arrays.equals(mSessionId, sid)) { 350 Log.e(TAG, "onExpirationUpdate callback: sessionId mismatch: |" + 351 Arrays.toString(mSessionId) + "| vs |" + Arrays.toString(sid) + "|"); 352 } else { 353 mExpirationUpdateReceived = true; 354 } 355 } 356 }, null); 357 mDrm.setOnSessionLostStateListener(new MediaDrm.OnSessionLostStateListener() { 358 @Override 359 public void onSessionLostState(MediaDrm md, byte[] sid) { 360 if (md != mDrm) { 361 Log.e(TAG, "onSessionLostState callback: drm object mismatch"); 362 } else if (!Arrays.equals(mSessionId, sid)) { 363 Log.e(TAG, "onSessionLostState callback: sessionId mismatch: |" + 364 Arrays.toString(mSessionId) + "| vs |" + Arrays.toString(sid) + "|"); 365 } else { 366 mLostStateReceived = true; 367 } 368 } 369 }, null); 370 mDrm.setOnKeyStatusChangeListener(new MediaDrm.OnKeyStatusChangeListener() { 371 @Override 372 public void onKeyStatusChange(MediaDrm md, byte[] sessionId, 373 List<KeyStatus> keyInformation, boolean hasNewUsableKey) { 374 Log.d(TAG, "onKeyStatusChange"); 375 assertTrue(md == mDrm); 376 assertTrue(Arrays.equals(sessionId, mSessionId)); 377 mSessionMonitor.signal(); 378 assertTrue(hasNewUsableKey); 379 380 assertEquals(3, keyInformation.size()); 381 KeyStatus keyStatus = keyInformation.get(0); 382 assertTrue(Arrays.equals(keyStatus.getKeyId(), new byte[] {0xa, 0xb, 0xc})); 383 assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_USABLE); 384 keyStatus = keyInformation.get(1); 385 assertTrue(Arrays.equals(keyStatus.getKeyId(), new byte[] {0xd, 0xe, 0xf})); 386 assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_EXPIRED); 387 keyStatus = keyInformation.get(2); 388 assertTrue(Arrays.equals(keyStatus.getKeyId(), new byte[] {0x0, 0x1, 0x2})); 389 assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_USABLE_IN_FUTURE); 390 } 391 }, null); 392 393 mLock.notify(); 394 } 395 Looper.loop(); // Blocks forever until Looper.quit() is called. 396 } 397 }.start(); 398 399 // wait for mDrm to be created 400 synchronized(mLock) { 401 try { 402 mLock.wait(1000); 403 } catch (Exception e) { 404 } 405 } 406 return mDrm; 407 } 408 stopDrm(MediaDrm drm)409 private void stopDrm(MediaDrm drm) { 410 if (drm != mDrm) { 411 Log.e(TAG, "invalid drm specified in stopDrm"); 412 } 413 mLooper.quit(); 414 mDrm.close(); 415 mDrm = null; 416 } 417 openSession(MediaDrm drm)418 private @NonNull byte[] openSession(MediaDrm drm) { 419 byte[] mSessionId = null; 420 boolean mRetryOpen; 421 do { 422 try { 423 mRetryOpen = false; 424 mSessionId = drm.openSession(); 425 } catch (Exception e) { 426 mRetryOpen = true; 427 } 428 } while (mRetryOpen); 429 return mSessionId; 430 } 431 closeSession(MediaDrm drm, byte[] sessionId)432 private void closeSession(MediaDrm drm, byte[] sessionId) { 433 drm.closeSession(sessionId); 434 } 435 436 /** 437 * Tests clear key system playback. 438 */ testClearKeyPlayback( UUID drmSchemeUuid, String videoMime, String[] videoFeatures, String initDataType, byte[][] clearKeyIds, Uri audioUrl, boolean audioEncrypted, Uri videoUrl, boolean videoEncrypted, int videoWidth, int videoHeight, boolean scrambled, int keyType)439 private void testClearKeyPlayback( 440 UUID drmSchemeUuid, 441 String videoMime, String[] videoFeatures, 442 String initDataType, byte[][] clearKeyIds, 443 Uri audioUrl, boolean audioEncrypted, 444 Uri videoUrl, boolean videoEncrypted, 445 int videoWidth, int videoHeight, boolean scrambled, int keyType) throws Exception { 446 447 if (isWatchDevice()) { 448 return; 449 } 450 451 MediaDrm drm = null; 452 mSessionId = null; 453 final boolean hasDrm = !scrambled && drmSchemeUuid != null; 454 if (hasDrm) { 455 drm = startDrm(clearKeyIds, initDataType, drmSchemeUuid, keyType); 456 mSessionId = openSession(drm); 457 } 458 459 if (!preparePlayback(videoMime, videoFeatures, audioUrl, audioEncrypted, videoUrl, 460 videoEncrypted, videoWidth, videoHeight, scrambled, mSessionId, getSurfaces())) { 461 // Allow device to skip test to keep existing behavior. 462 // We should throw an exception for new tests. 463 return; 464 } 465 466 if (hasDrm) { 467 mDrmInitData = mMediaCodecPlayer.getDrmInitData(); 468 getKeys(mDrm, initDataType, mSessionId, mDrmInitData, keyType, clearKeyIds); 469 } 470 471 if (hasDrm && keyType == MediaDrm.KEY_TYPE_OFFLINE) { 472 closeSession(drm, mSessionId); 473 mSessionMonitor.waitForSignal(); 474 mSessionId = openSession(drm); 475 if (mKeySetId.length > 0) { 476 drm.restoreKeys(mSessionId, mKeySetId); 477 } else { 478 closeSession(drm, mSessionId); 479 stopDrm(drm); 480 throw new Error("Invalid keySetId size for offline license"); 481 } 482 } 483 484 // starts video playback 485 playUntilEnd(); 486 if (hasDrm) { 487 closeSession(drm, mSessionId); 488 stopDrm(drm); 489 } 490 } 491 492 /** 493 * Tests KEY_TYPE_RELEASE for offline license. 494 */ 495 @Presubmit 496 @FrameworkSpecificTest testReleaseOfflineLicense()497 public void testReleaseOfflineLicense() throws Exception { 498 if (isWatchDevice()) { 499 return; 500 } 501 502 byte[][] clearKeyIds = new byte[][] { CLEAR_KEY_CENC }; 503 mSessionId = null; 504 String initDataType = "cenc"; 505 506 MediaDrm drm = startDrm(clearKeyIds, initDataType, 507 CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_OFFLINE); 508 mSessionId = openSession(drm); 509 510 if (false == playbackPreCheck(MIME_VIDEO_AVC, 511 new String[] { CodecCapabilities.FEATURE_SecurePlayback }, CENC_VIDEO_URL, 512 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC)) { 513 // retry with unsecure codec 514 if (false == playbackPreCheck(MIME_VIDEO_AVC, 515 new String[0], CENC_VIDEO_URL, 516 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC)) { 517 Log.e(TAG, "Failed playback precheck"); 518 return; 519 } 520 } 521 522 mMediaCodecPlayer = new MediaCodecClearKeyPlayer( 523 getSurfaces(), 524 mSessionId, false /*scrambled */, 525 mContext); 526 527 mMediaCodecPlayer.setAudioDataSource(CENC_AUDIO_URL, null, false); 528 mMediaCodecPlayer.setVideoDataSource(CENC_VIDEO_URL, null, true); 529 mMediaCodecPlayer.start(); 530 if (!mMediaCodecPlayer.prepare()) { 531 Log.i(TAG, "Media Player could not be prepared."); 532 return; 533 } 534 mDrmInitData = mMediaCodecPlayer.getDrmInitData(); 535 536 // Create and store the offline license 537 getKeys(mDrm, initDataType, mSessionId, mDrmInitData, MediaDrm.KEY_TYPE_OFFLINE, 538 clearKeyIds); 539 540 // Verify the offline license is valid 541 closeSession(drm, mSessionId); 542 mSessionMonitor.waitForSignal(); 543 mDrm.clearOnKeyStatusChangeListener(); 544 mSessionId = openSession(drm); 545 drm.restoreKeys(mSessionId, mKeySetId); 546 closeSession(drm, mSessionId); 547 548 // Release the offline license 549 getKeys(mDrm, initDataType, mKeySetId, mDrmInitData, MediaDrm.KEY_TYPE_RELEASE, 550 clearKeyIds); 551 552 // Verify restoreKeys will throw an exception if the offline license 553 // has already been released 554 mSessionId = openSession(drm); 555 try { 556 drm.restoreKeys(mSessionId, mKeySetId); 557 } catch (MediaDrmStateException e) { 558 // Expected exception caught, all is good 559 return; 560 } finally { 561 closeSession(drm, mSessionId); 562 stopDrm(drm); 563 } 564 565 // Did not receive expected exception, throw an Error 566 throw new Error("Did not receive expected exception from restoreKeys"); 567 } 568 queryKeyStatus(@onNull final MediaDrm drm, @NonNull final byte[] sessionId)569 private boolean queryKeyStatus(@NonNull final MediaDrm drm, @NonNull final byte[] sessionId) { 570 final HashMap<String, String> keyStatus = drm.queryKeyStatus(sessionId); 571 if (keyStatus.isEmpty()) { 572 Log.e(TAG, "queryKeyStatus: empty key status"); 573 return false; 574 } 575 576 final Set<String> keySet = keyStatus.keySet(); 577 final int numKeys = keySet.size(); 578 final String[] keys = keySet.toArray(new String[numKeys]); 579 for (int i = 0; i < numKeys; ++i) { 580 final String key = keys[i]; 581 Log.i(TAG, "queryKeyStatus: key=" + key + ", value=" + keyStatus.get(key)); 582 } 583 584 return true; 585 } 586 587 @Presubmit 588 @FrameworkSpecificTest testQueryKeyStatus()589 public void testQueryKeyStatus() throws Exception { 590 if (isWatchDevice()) { 591 // skip this test on watch because it calls 592 // addTrack that requires codec 593 return; 594 } 595 596 MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC }, "cenc", 597 CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_STREAMING); 598 599 mSessionId = openSession(drm); 600 601 // Test default key status, should not be defined 602 final HashMap<String, String> keyStatus = drm.queryKeyStatus(mSessionId); 603 if (!keyStatus.isEmpty()) { 604 closeSession(drm, mSessionId); 605 stopDrm(drm); 606 throw new Error("query default key status failed"); 607 } 608 609 // Test valid key status 610 mMediaCodecPlayer = new MediaCodecClearKeyPlayer( 611 getSurfaces(), 612 mSessionId, false, 613 mContext); 614 mMediaCodecPlayer.setAudioDataSource(CENC_AUDIO_URL, null, false); 615 mMediaCodecPlayer.setVideoDataSource(CENC_VIDEO_URL, null, true); 616 mMediaCodecPlayer.start(); 617 if (!mMediaCodecPlayer.prepare()) { 618 Log.i(TAG, "Media Player could not be prepared."); 619 return; 620 } 621 622 mDrmInitData = mMediaCodecPlayer.getDrmInitData(); 623 getKeys(drm, "cenc", mSessionId, mDrmInitData, MediaDrm.KEY_TYPE_STREAMING, 624 new byte[][] { CLEAR_KEY_CENC }); 625 boolean success = true; 626 if (!queryKeyStatus(drm, mSessionId)) { 627 success = false; 628 } 629 630 mMediaCodecPlayer.reset(); 631 closeSession(drm, mSessionId); 632 stopDrm(drm); 633 if (!success) { 634 throw new Error("query key status failed"); 635 } 636 } 637 638 @Presubmit 639 @FrameworkSpecificTest testOfflineKeyManagement()640 public void testOfflineKeyManagement() throws Exception { 641 if (isWatchDevice()) { 642 // skip this test on watch because it calls 643 // addTrack that requires codec 644 return; 645 } 646 647 MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC }, "cenc", 648 CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_OFFLINE); 649 650 if (getClearkeyVersion(drm).matches("1.[01]")) { 651 Log.i(TAG, "Skipping testsOfflineKeyManagement: clearkey 1.2 required"); 652 return; 653 } 654 655 mSessionId = openSession(drm); 656 657 // Test get offline keys 658 mMediaCodecPlayer = new MediaCodecClearKeyPlayer( 659 getSurfaces(), 660 mSessionId, false, 661 mContext); 662 mMediaCodecPlayer.setAudioDataSource(CENC_AUDIO_URL, null, false); 663 mMediaCodecPlayer.setVideoDataSource(CENC_VIDEO_URL, null, true); 664 mMediaCodecPlayer.start(); 665 if (!mMediaCodecPlayer.prepare()) { 666 Log.i(TAG, "Media Player could not be prepared."); 667 return; 668 } 669 670 try { 671 mDrmInitData = mMediaCodecPlayer.getDrmInitData(); 672 673 List<byte[]> keySetIds = drm.getOfflineLicenseKeySetIds(); 674 int preCount = keySetIds.size(); 675 676 getKeys(drm, "cenc", mSessionId, mDrmInitData, MediaDrm.KEY_TYPE_OFFLINE, 677 new byte[][] { CLEAR_KEY_CENC }); 678 679 if (drm.getOfflineLicenseState(mKeySetId) != MediaDrm.OFFLINE_LICENSE_STATE_USABLE) { 680 throw new Error("Offline license state is not usable"); 681 } 682 683 keySetIds = drm.getOfflineLicenseKeySetIds(); 684 685 if (keySetIds.size() != preCount + 1) { 686 throw new Error("KeySetIds size did not increment"); 687 } 688 689 boolean found = false; 690 for (int i = 0; i < keySetIds.size(); i++) { 691 if (Arrays.equals(keySetIds.get(i), mKeySetId)) { 692 found = true; 693 break; 694 } 695 } 696 if (!found) { 697 throw new Error("New KeySetId is missing from KeySetIds"); 698 } 699 700 drm.removeOfflineLicense(mKeySetId); 701 702 keySetIds = drm.getOfflineLicenseKeySetIds(); 703 if (keySetIds.size() != preCount) { 704 throw new Error("KeySetIds size is incorrect"); 705 } 706 707 found = false; 708 for (int i = 0; i < keySetIds.size(); i++) { 709 if (Arrays.equals(keySetIds.get(i), mKeySetId)) { 710 found = true; 711 break; 712 } 713 } 714 715 if (found) { 716 throw new Error("New KeySetId is still in from KeySetIds after removal"); 717 } 718 719 // TODO: after RELEASE is implemented: add offline key, release it 720 // get offline key status, check state is inactive 721 } finally { 722 mMediaCodecPlayer.reset(); 723 closeSession(drm, mSessionId); 724 stopDrm(drm); 725 } 726 } 727 728 // returns FEATURE_SecurePlayback if device supports secure codec, 729 // else returns an empty string for the codec feature determineCodecFeatures(String mime, int videoWidth, int videoHeight)730 private String[] determineCodecFeatures(String mime, 731 int videoWidth, int videoHeight) { 732 String[] codecFeatures = { CodecCapabilities.FEATURE_SecurePlayback }; 733 if (!isResolutionSupported(MIME_VIDEO_AVC, codecFeatures, 734 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC)) { 735 // for device that does not support secure codec 736 codecFeatures = new String[0]; 737 } 738 return codecFeatures; 739 } 740 741 @FrameworkSpecificTest testClearKeyPlaybackCenc()742 public void testClearKeyPlaybackCenc() throws Exception { 743 String[] codecFeatures = determineCodecFeatures(MIME_VIDEO_AVC, 744 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC); 745 testClearKeyPlayback( 746 COMMON_PSSH_SCHEME_UUID, 747 // using secure codec even though it is clear key DRM 748 MIME_VIDEO_AVC, codecFeatures, 749 "cenc", new byte[][]{CLEAR_KEY_CENC}, 750 CENC_AUDIO_URL, false /* audioEncrypted */, 751 CENC_VIDEO_URL, true /* videoEncrypted */, 752 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false /* scrambled */, 753 MediaDrm.KEY_TYPE_STREAMING); 754 } 755 756 @Presubmit 757 @FrameworkSpecificTest testClearKeyPlaybackCenc2()758 public void testClearKeyPlaybackCenc2() throws Exception { 759 String[] codecFeatures = determineCodecFeatures(MIME_VIDEO_AVC, 760 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC); 761 testClearKeyPlayback( 762 CLEARKEY_SCHEME_UUID, 763 // using secure codec even though it is clear key DRM 764 MIME_VIDEO_AVC, codecFeatures, 765 "cenc", new byte[][]{CLEAR_KEY_CENC}, 766 CENC_AUDIO_URL, false /* audioEncrypted */, 767 CENC_VIDEO_URL, true /* videoEncrypted */, 768 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false /* scrambled */, 769 MediaDrm.KEY_TYPE_STREAMING); 770 } 771 772 @Presubmit 773 @FrameworkSpecificTest testClearKeyPlaybackOfflineCenc()774 public void testClearKeyPlaybackOfflineCenc() throws Exception { 775 String[] codecFeatures = determineCodecFeatures(MIME_VIDEO_AVC, 776 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC); 777 testClearKeyPlayback( 778 CLEARKEY_SCHEME_UUID, 779 // using secure codec even though it is clear key DRM 780 MIME_VIDEO_AVC, codecFeatures, 781 "cenc", new byte[][]{CLEAR_KEY_CENC}, 782 CENC_AUDIO_URL, false /* audioEncrypted */, 783 CENC_VIDEO_URL, true /* videoEncrypted */, 784 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false /* scrambled */, 785 MediaDrm.KEY_TYPE_OFFLINE); 786 } 787 788 @FrameworkSpecificTest testClearKeyPlaybackWebm()789 public void testClearKeyPlaybackWebm() throws Exception { 790 testClearKeyPlayback( 791 CLEARKEY_SCHEME_UUID, 792 MIME_VIDEO_VP8, new String[0], 793 "webm", new byte[][]{CLEAR_KEY_WEBM}, 794 WEBM_URL, true /* audioEncrypted */, 795 WEBM_URL, true /* videoEncrypted */, 796 VIDEO_WIDTH_WEBM, VIDEO_HEIGHT_WEBM, false /* scrambled */, 797 MediaDrm.KEY_TYPE_STREAMING); 798 } 799 800 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 801 @ModuleSpecificTest testClearKeyPlaybackMpeg2ts()802 public void testClearKeyPlaybackMpeg2ts() throws Exception { 803 testClearKeyPlayback( 804 CLEARKEY_SCHEME_UUID, 805 MIME_VIDEO_AVC, new String[0], 806 "mpeg2ts", null, 807 MPEG2TS_SCRAMBLED_URL, false /* audioEncrypted */, 808 MPEG2TS_SCRAMBLED_URL, false /* videoEncrypted */, 809 VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, true /* scrambled */, 810 MediaDrm.KEY_TYPE_STREAMING); 811 } 812 813 @ModuleSpecificTest testPlaybackMpeg2ts()814 public void testPlaybackMpeg2ts() throws Exception { 815 testClearKeyPlayback( 816 CLEARKEY_SCHEME_UUID, 817 MIME_VIDEO_AVC, new String[0], 818 "mpeg2ts", null, 819 MPEG2TS_CLEAR_URL, false /* audioEncrypted */, 820 MPEG2TS_CLEAR_URL, false /* videoEncrypted */, 821 VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, false /* scrambled */, 822 MediaDrm.KEY_TYPE_STREAMING); 823 } 824 getStringProperty(final MediaDrm drm, final String key)825 private String getStringProperty(final MediaDrm drm, final String key) { 826 String value = ""; 827 try { 828 value = drm.getPropertyString(key); 829 } catch (IllegalArgumentException e) { 830 // Expected exception for invalid key 831 Log.d(TAG, "Expected result: " + e.getMessage()); 832 } catch (Exception e) { 833 throw new Error(e.getMessage() + "-" + key); 834 } 835 return value; 836 } 837 getByteArrayProperty(final MediaDrm drm, final String key)838 private byte[] getByteArrayProperty(final MediaDrm drm, final String key) { 839 byte[] bytes = new byte[0]; 840 try { 841 bytes = drm.getPropertyByteArray(key); 842 } catch (IllegalArgumentException e) { 843 // Expected exception for invalid key 844 Log.d(TAG, "Expected: " + e.getMessage() + " - " + key); 845 } catch (Exception e) { 846 throw new Error(e.getMessage() + "-" + key); 847 } 848 return bytes; 849 } 850 setStringProperty(final MediaDrm drm, final String key, final String value)851 private void setStringProperty(final MediaDrm drm, final String key, final String value) { 852 try { 853 drm.setPropertyString(key, value); 854 } catch (IllegalArgumentException e) { 855 // Expected exception for invalid key 856 Log.d(TAG, "Expected: " + e.getMessage() + " - " + key); 857 } catch (Exception e) { 858 throw new Error(e.getMessage() + "-" + key); 859 } 860 } 861 setByteArrayProperty(final MediaDrm drm, final String key, final byte[] bytes)862 private void setByteArrayProperty(final MediaDrm drm, final String key, final byte[] bytes) { 863 try { 864 drm.setPropertyByteArray(key, bytes); 865 } catch (IllegalArgumentException e) { 866 // Expected exception for invalid key 867 Log.d(TAG, "Expected: " + e.getMessage() + " - " + key); 868 } catch (Exception e) { 869 throw new Error(e.getMessage() + "-" + key); 870 } 871 } 872 873 @Presubmit 874 @FrameworkSpecificTest testGetProperties()875 public void testGetProperties() throws Exception { 876 if (watchHasNoClearkeySupport()) { 877 return; 878 } 879 880 MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC }, 881 "cenc", CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_STREAMING); 882 883 try { 884 // The following tests will not verify the value we are getting 885 // back since it could change in the future. 886 final String[] sKeys = { 887 DESCRIPTION_PROPERTY_KEY, LISTENER_TEST_SUPPORT_PROPERTY_KEY, 888 VENDOR_PROPERTY_KEY, VERSION_PROPERTY_KEY}; 889 String value; 890 for (String key : sKeys) { 891 value = getStringProperty(drm, key); 892 Log.d(TAG, "getPropertyString returns: " + key + ", " + value); 893 if (value.isEmpty()) { 894 throw new Error("Failed to get property for: " + key); 895 } 896 } 897 898 if (cannotHandleGetPropertyByteArray(drm)) { 899 Log.i(TAG, "Skipping testGetProperties: byte array properties not implemented " 900 + "on devices launched before P"); 901 return; 902 } 903 904 byte[] bytes = getByteArrayProperty(drm, DEVICEID_PROPERTY_KEY); 905 if (0 == bytes.length) { 906 throw new Error("Failed to get property for: " + DEVICEID_PROPERTY_KEY); 907 } 908 909 // Test with an invalid property key. 910 value = getStringProperty(drm, INVALID_PROPERTY_KEY); 911 bytes = getByteArrayProperty(drm, INVALID_PROPERTY_KEY); 912 if (!value.isEmpty() || 0 != bytes.length) { 913 throw new Error("get property failed using an invalid property key"); 914 } 915 } finally { 916 stopDrm(drm); 917 } 918 } 919 920 @Presubmit 921 @FrameworkSpecificTest testSetProperties()922 public void testSetProperties() throws Exception { 923 if (watchHasNoClearkeySupport()) { 924 return; 925 } 926 927 MediaDrm drm = startDrm(new byte[][]{CLEAR_KEY_CENC}, 928 "cenc", CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_STREAMING); 929 930 try { 931 if (cannotHandleSetPropertyString(drm)) { 932 Log.i(TAG, "Skipping testSetProperties: set property string not implemented " 933 + "on devices launched before P"); 934 return; 935 } 936 937 // Test setting predefined string property 938 // - Save the value to be restored later 939 // - Set the property value 940 // - Check the value that was set 941 // - Restore previous value 942 String listenerTestSupport = getStringProperty(drm, LISTENER_TEST_SUPPORT_PROPERTY_KEY); 943 944 setStringProperty(drm, LISTENER_TEST_SUPPORT_PROPERTY_KEY, "testing"); 945 946 String value = getStringProperty(drm, LISTENER_TEST_SUPPORT_PROPERTY_KEY); 947 if (!value.equals("testing")) { 948 throw new Error("Failed to set property: " + LISTENER_TEST_SUPPORT_PROPERTY_KEY); 949 } 950 951 setStringProperty(drm, LISTENER_TEST_SUPPORT_PROPERTY_KEY, listenerTestSupport); 952 953 // Test setting immutable properties 954 HashMap<String, String> defaultImmutableProperties = new HashMap<String, String>(); 955 defaultImmutableProperties.put(ALGORITHMS_PROPERTY_KEY, 956 getStringProperty(drm, ALGORITHMS_PROPERTY_KEY)); 957 defaultImmutableProperties.put(DESCRIPTION_PROPERTY_KEY, 958 getStringProperty(drm, DESCRIPTION_PROPERTY_KEY)); 959 defaultImmutableProperties.put(VENDOR_PROPERTY_KEY, 960 getStringProperty(drm, VENDOR_PROPERTY_KEY)); 961 defaultImmutableProperties.put(VERSION_PROPERTY_KEY, 962 getStringProperty(drm, VERSION_PROPERTY_KEY)); 963 964 HashMap<String, String> immutableProperties = new HashMap<String, String>(); 965 immutableProperties.put(ALGORITHMS_PROPERTY_KEY, "brute force"); 966 immutableProperties.put(DESCRIPTION_PROPERTY_KEY, "testing only"); 967 immutableProperties.put(VENDOR_PROPERTY_KEY, "my Google"); 968 immutableProperties.put(VERSION_PROPERTY_KEY, "undefined"); 969 970 for (String key : immutableProperties.keySet()) { 971 setStringProperty(drm, key, immutableProperties.get(key)); 972 } 973 974 // Verify the immutable properties have not been set 975 for (String key : immutableProperties.keySet()) { 976 value = getStringProperty(drm, key); 977 if (!defaultImmutableProperties.get(key).equals(getStringProperty(drm, key))) { 978 throw new Error("Immutable property has changed, key=" + key); 979 } 980 } 981 982 // Test setPropertyByteArray for immutable property 983 final byte[] bytes = new byte[] { 984 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 985 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0}; 986 987 final byte[] deviceId = getByteArrayProperty(drm, DEVICEID_PROPERTY_KEY); 988 989 setByteArrayProperty(drm, DEVICEID_PROPERTY_KEY, bytes); 990 991 // Verify deviceId has not changed 992 if (!Arrays.equals(deviceId, getByteArrayProperty(drm, DEVICEID_PROPERTY_KEY))) { 993 throw new Error("Failed to set byte array for key=" + DEVICEID_PROPERTY_KEY); 994 } 995 996 for (String k: new String[] {"oemError", "errorContext"}) { 997 testIntegerProperties(drm, k); 998 } 999 } finally { 1000 stopDrm(drm); 1001 } 1002 } 1003 1004 private final static int CLEARKEY_MAX_SESSIONS = 10; 1005 1006 @Presubmit 1007 @FrameworkSpecificTest testGetNumberOfSessions()1008 public void testGetNumberOfSessions() { 1009 if (watchHasNoClearkeySupport()) { 1010 return; 1011 } 1012 1013 MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC }, 1014 "cenc", CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_STREAMING); 1015 1016 try { 1017 if (getClearkeyVersion(drm).equals("1.0")) { 1018 Log.i(TAG, "Skipping testGetNumberOfSessions: not supported by clearkey 1.0"); 1019 return; 1020 } 1021 1022 int maxSessionCount = drm.getMaxSessionCount(); 1023 if (maxSessionCount != CLEARKEY_MAX_SESSIONS) { 1024 throw new Error("expected max session count to be " + 1025 CLEARKEY_MAX_SESSIONS); 1026 } 1027 int initialOpenSessionCount = drm.getOpenSessionCount(); 1028 if (initialOpenSessionCount == maxSessionCount) { 1029 throw new Error("all sessions open, can't do increment test"); 1030 } 1031 mSessionId = openSession(drm); 1032 try { 1033 if (drm.getOpenSessionCount() != initialOpenSessionCount + 1) { 1034 throw new Error("openSessionCount didn't increment"); 1035 } 1036 } finally { 1037 closeSession(drm, mSessionId); 1038 } 1039 } finally { 1040 stopDrm(drm); 1041 } 1042 } 1043 1044 @Presubmit 1045 @FrameworkSpecificTest testHdcpLevels()1046 public void testHdcpLevels() { 1047 if (watchHasNoClearkeySupport()) { 1048 return; 1049 } 1050 1051 MediaDrm drm = null; 1052 try { 1053 drm = new MediaDrm(CLEARKEY_SCHEME_UUID); 1054 1055 if (getClearkeyVersion(drm).equals("1.0")) { 1056 Log.i(TAG, "Skipping testHdcpLevels: not supported by clearkey 1.0"); 1057 return; 1058 } 1059 1060 if (drm.getConnectedHdcpLevel() != MediaDrm.HDCP_NONE) { 1061 throw new Error("expected connected hdcp level to be HDCP_NONE"); 1062 } 1063 1064 if (drm.getMaxHdcpLevel() != MediaDrm.HDCP_NO_DIGITAL_OUTPUT) { 1065 throw new Error("expected max hdcp level to be HDCP_NO_DIGITAL_OUTPUT"); 1066 } 1067 } catch(Exception e) { 1068 throw new Error("Unexpected exception ", e); 1069 } finally { 1070 if (drm != null) { 1071 drm.close(); 1072 } 1073 } 1074 } 1075 1076 @Presubmit 1077 @FrameworkSpecificTest testSecurityLevels()1078 public void testSecurityLevels() { 1079 if (watchHasNoClearkeySupport()) { 1080 return; 1081 } 1082 1083 MediaDrm drm = null; 1084 byte[] sessionId = null; 1085 try { 1086 drm = new MediaDrm(CLEARKEY_SCHEME_UUID); 1087 1088 if (getClearkeyVersion(drm).equals("1.0")) { 1089 Log.i(TAG, "Skipping testSecurityLevels: not supported by clearkey 1.0"); 1090 return; 1091 } 1092 1093 sessionId = drm.openSession(MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO); 1094 if (drm.getSecurityLevel(sessionId) != MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO) { 1095 throw new Error("expected security level to be SECURITY_LEVEL_SW_SECURE_CRYPTO"); 1096 } 1097 drm.closeSession(sessionId); 1098 sessionId = null; 1099 1100 sessionId = drm.openSession(); 1101 if (drm.getSecurityLevel(sessionId) != MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO) { 1102 throw new Error("expected security level to be SECURITY_LEVEL_SW_SECURE_CRYPTO"); 1103 } 1104 drm.closeSession(sessionId); 1105 sessionId = null; 1106 1107 try { 1108 sessionId = drm.openSession(MediaDrm.SECURITY_LEVEL_SW_SECURE_DECODE); 1109 } catch (IllegalArgumentException e) { 1110 /* caught expected exception */ 1111 } catch (Exception e) { 1112 throw new Exception ("did't get expected IllegalArgumentException" + 1113 " while opening a session with disallowed security level"); 1114 } finally { 1115 if (sessionId != null) { 1116 drm.closeSession(sessionId); 1117 sessionId = null; 1118 } 1119 } 1120 } catch(Exception e) { 1121 throw new Error("Unexpected exception ", e); 1122 } finally { 1123 if (sessionId != null) { 1124 drm.closeSession(sessionId); 1125 } 1126 if (drm != null) { 1127 drm.close(); 1128 } 1129 } 1130 } 1131 1132 @Presubmit 1133 @FrameworkSpecificTest testSecureStop()1134 public void testSecureStop() { 1135 if (watchHasNoClearkeySupport()) { 1136 return; 1137 } 1138 1139 MediaDrm drm = startDrm(new byte[][] {CLEAR_KEY_CENC}, "cenc", 1140 CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_STREAMING); 1141 1142 byte[] sessionId = null; 1143 try { 1144 if (getClearkeyVersion(drm).equals("1.0")) { 1145 Log.i(TAG, "Skipping testSecureStop: not supported in ClearKey v1.0"); 1146 return; 1147 } 1148 1149 drm.removeAllSecureStops(); 1150 Log.d(TAG, "Test getSecureStops from an empty list."); 1151 List<byte[]> secureStops = drm.getSecureStops(); 1152 assertTrue(secureStops.isEmpty()); 1153 1154 Log.d(TAG, "Test getSecureStopIds from an empty list."); 1155 List<byte[]> secureStopIds = drm.getSecureStopIds(); 1156 assertTrue(secureStopIds.isEmpty()); 1157 1158 mSessionId = openSession(drm); 1159 1160 mMediaCodecPlayer = new MediaCodecClearKeyPlayer( 1161 getSurfaces(), mSessionId, false, mContext); 1162 mMediaCodecPlayer.setAudioDataSource(CENC_AUDIO_URL, null, false); 1163 mMediaCodecPlayer.setVideoDataSource(CENC_VIDEO_URL, null, true); 1164 mMediaCodecPlayer.start(); 1165 if (!mMediaCodecPlayer.prepare()) { 1166 Log.i(TAG, "Media Player could not be prepared."); 1167 return; 1168 } 1169 mDrmInitData = mMediaCodecPlayer.getDrmInitData(); 1170 1171 for (int i = 0; i < NUMBER_OF_SECURE_STOPS; ++i) { 1172 getKeys(drm, "cenc", mSessionId, mDrmInitData, 1173 MediaDrm.KEY_TYPE_STREAMING, new byte[][] {CLEAR_KEY_CENC}); 1174 } 1175 Log.d(TAG, "Test getSecureStops."); 1176 secureStops = drm.getSecureStops(); 1177 assertEquals(NUMBER_OF_SECURE_STOPS, secureStops.size()); 1178 1179 Log.d(TAG, "Test getSecureStopIds."); 1180 secureStopIds = drm.getSecureStopIds(); 1181 assertEquals(NUMBER_OF_SECURE_STOPS, secureStopIds.size()); 1182 1183 Log.d(TAG, "Test getSecureStop using secure stop Ids."); 1184 for (int i = 0; i < secureStops.size(); ++i) { 1185 byte[] secureStop = drm.getSecureStop(secureStopIds.get(i)); 1186 assertTrue(Arrays.equals(secureStops.get(i), secureStop)); 1187 } 1188 1189 Log.d(TAG, "Test removeSecureStop given a secure stop Id."); 1190 drm.removeSecureStop(secureStopIds.get(NUMBER_OF_SECURE_STOPS - 1)); 1191 secureStops = drm.getSecureStops(); 1192 secureStopIds = drm.getSecureStopIds(); 1193 assertEquals(NUMBER_OF_SECURE_STOPS - 1, secureStops.size()); 1194 assertEquals(NUMBER_OF_SECURE_STOPS - 1, secureStopIds.size()); 1195 1196 Log.d(TAG, "Test releaseSecureStops given a release message."); 1197 // Simulate server response message by removing 1198 // every other secure stops to make it interesting. 1199 List<byte[]> releaseList = new ArrayList<byte[]>(); 1200 int releaseListSize = 0; 1201 for (int i = 0; i < secureStops.size(); i += 2) { 1202 byte[] secureStop = secureStops.get(i); 1203 releaseList.add(secureStop); 1204 releaseListSize += secureStop.length; 1205 } 1206 1207 // ClearKey's release message format (this is a format shared between 1208 // the server and the drm service). 1209 // The clearkey implementation expects the message to contain 1210 // a 4 byte count of the number of fixed length secure stops 1211 // to follow. 1212 String count = String.format("%04d", releaseList.size()); 1213 byte[] releaseMessage = new byte[count.length() + releaseListSize]; 1214 1215 byte[] buffer = count.getBytes(); 1216 System.arraycopy(buffer, 0, releaseMessage, 0, count.length()); 1217 1218 int destPosition = count.length(); 1219 for (int i = 0; i < releaseList.size(); ++i) { 1220 byte[] secureStop = releaseList.get(i); 1221 int secureStopSize = secureStop.length; 1222 System.arraycopy(secureStop, 0, releaseMessage, destPosition, secureStopSize); 1223 destPosition += secureStopSize; 1224 } 1225 1226 drm.releaseSecureStops(releaseMessage); 1227 secureStops = drm.getSecureStops(); 1228 secureStopIds = drm.getSecureStopIds(); 1229 // All odd numbered secure stops are removed in the test, 1230 // leaving 2nd, 4th, 6th and the 8th element. 1231 assertEquals((NUMBER_OF_SECURE_STOPS - 1) / 2, secureStops.size()); 1232 assertEquals((NUMBER_OF_SECURE_STOPS - 1 ) / 2, secureStopIds.size()); 1233 1234 Log.d(TAG, "Test removeAllSecureStops."); 1235 drm.removeAllSecureStops(); 1236 secureStops = drm.getSecureStops(); 1237 assertTrue(secureStops.isEmpty()); 1238 secureStopIds = drm.getSecureStopIds(); 1239 assertTrue(secureStopIds.isEmpty()); 1240 1241 mMediaCodecPlayer.reset(); 1242 closeSession(drm, mSessionId); 1243 } catch (Exception e) { 1244 throw new Error("Unexpected exception", e); 1245 } finally { 1246 if (sessionId != null) { 1247 drm.closeSession(sessionId); 1248 } 1249 stopDrm(drm); 1250 } 1251 } 1252 1253 /** 1254 * Test that the framework handles a device returning 1255 * ::android::hardware::drm@1.2::Status::ERROR_DRM_RESOURCE_CONTENTION. 1256 * Expected behavior: throws MediaDrm.SessionException with 1257 * errorCode ERROR_RESOURCE_CONTENTION 1258 */ 1259 @Presubmit 1260 @FrameworkSpecificTest testResourceContentionError()1261 public void testResourceContentionError() { 1262 1263 if (watchHasNoClearkeySupport()) { 1264 return; 1265 } 1266 1267 MediaDrm drm = null; 1268 boolean gotException = false; 1269 final int OEM_ERROR = 123; 1270 final int ERROR_CONTEXT = 456; 1271 1272 try { 1273 drm = new MediaDrm(CLEARKEY_SCHEME_UUID); 1274 drm.setPropertyString("drmErrorTest", "resourceContention"); 1275 if (getClearkeyVersionInt(drm) >= 14) { 1276 drm.setPropertyString("oemError", Integer.toString(OEM_ERROR)); 1277 drm.setPropertyString("errorContext", Integer.toString(ERROR_CONTEXT)); 1278 } 1279 byte[] sessionId = drm.openSession(); 1280 1281 try { 1282 byte[] ignoredInitData = new byte[] { 1 }; 1283 drm.getKeyRequest(sessionId, ignoredInitData, "cenc", MediaDrm.KEY_TYPE_STREAMING, null); 1284 } catch (MediaDrm.SessionException e) { 1285 if (e.getErrorCode() != MediaDrm.SessionException.ERROR_RESOURCE_CONTENTION) { 1286 throw new Error("Expected transient ERROR_RESOURCE_CONTENTION"); 1287 } 1288 if(sIsAtLeastS && !e.isTransient()) { 1289 throw new Error("Expected transient ERROR_RESOURCE_CONTENTION"); 1290 } 1291 if (getClearkeyVersionInt(drm) >= 14) { 1292 final MediaDrmThrowable mdt = e; 1293 final int RESOURCE_CONTENTION_AIDL = 16; 1294 assertEquals("Vendor Error mismatch", mdt.getVendorError(), RESOURCE_CONTENTION_AIDL); 1295 assertEquals("OEM Error mismatch", mdt.getOemError(), OEM_ERROR); 1296 assertEquals("Error context mismatch", mdt.getErrorContext(), ERROR_CONTEXT); 1297 } 1298 gotException = true; 1299 } 1300 } catch(Exception e) { 1301 throw new Error("Unexpected exception ", e); 1302 } finally { 1303 if (drm != null) { 1304 drm.close(); 1305 } 1306 } 1307 if (!gotException) { 1308 throw new Error("Didn't receive expected MediaDrm.SessionException"); 1309 } 1310 } 1311 1312 /** 1313 * Test sendExpirationUpdate and onExpirationUpdateListener 1314 * 1315 * Expected behavior: the EXPIRATION_UPDATE event arrives 1316 * at the onExpirationUpdateListener with the expiry time 1317 */ 1318 @Presubmit 1319 @FrameworkSpecificTest testOnExpirationUpdateListener()1320 public void testOnExpirationUpdateListener() { 1321 1322 if (watchHasNoClearkeySupport()) { 1323 return; 1324 } 1325 1326 MediaDrm drm = null; 1327 mSessionId = null; 1328 mExpirationUpdateReceived = false; 1329 1330 // provideKeyResponse calls sendExpirationUpdate method 1331 // for testing purpose, we therefore start a license request 1332 // which calls provideKeyResonpse 1333 byte[][] clearKeyIds = new byte[][] { CLEAR_KEY_CENC }; 1334 int keyType = MediaDrm.KEY_TYPE_STREAMING; 1335 String initDataType = new String("cenc"); 1336 1337 drm = startDrm(clearKeyIds, initDataType, CLEARKEY_SCHEME_UUID, keyType); 1338 mSessionId = openSession(drm); 1339 try { 1340 if (!preparePlayback( 1341 MIME_VIDEO_AVC, 1342 new String[0], 1343 CENC_AUDIO_URL, false /* audioEncrypted */ , 1344 CENC_VIDEO_URL, true /* videoEncrypted */, 1345 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false /* scrambled */, 1346 mSessionId, getSurfaces())) { 1347 closeSession(drm, mSessionId); 1348 stopDrm(drm); 1349 return; 1350 } 1351 } catch (Exception e) { 1352 throw new Error("Unexpected exception ", e); 1353 } 1354 1355 mDrmInitData = mMediaCodecPlayer.getDrmInitData(); 1356 getKeys(drm, initDataType, mSessionId, mDrmInitData, 1357 keyType, clearKeyIds); 1358 1359 // wait for the event to arrive 1360 try { 1361 closeSession(drm, mSessionId); 1362 // wait up to 2 seconds for event 1363 for (int i = 0; i < 20 && !mExpirationUpdateReceived; i++) { 1364 try { 1365 Thread.sleep(100); 1366 } catch (InterruptedException e) { 1367 } 1368 } 1369 if (!mExpirationUpdateReceived) { 1370 throw new Error("EXPIRATION_UPDATE event was not received by the listener"); 1371 } 1372 } catch (MediaDrmStateException e) { 1373 throw new Error("Unexpected exception from closing session: ", e); 1374 } finally { 1375 stopDrm(drm); 1376 } 1377 } 1378 1379 /** 1380 * Test that the onExpirationUpdateListener 1381 * listener is not called after 1382 * clearOnExpirationUpdateListener is called. 1383 */ 1384 @Presubmit 1385 @FrameworkSpecificTest testClearOnExpirationUpdateListener()1386 public void testClearOnExpirationUpdateListener() { 1387 1388 if (watchHasNoClearkeySupport()) { 1389 return; 1390 } 1391 1392 MediaDrm drm = null; 1393 mSessionId = null; 1394 mExpirationUpdateReceived = false; 1395 1396 // provideKeyResponse calls sendExpirationUpdate method 1397 // for testing purpose, we therefore start a license request 1398 // which calls provideKeyResonpse 1399 byte[][] clearKeyIds = new byte[][] { CLEAR_KEY_CENC }; 1400 int keyType = MediaDrm.KEY_TYPE_STREAMING; 1401 String initDataType = new String("cenc"); 1402 1403 drm = startDrm(clearKeyIds, initDataType, CLEARKEY_SCHEME_UUID, keyType); 1404 mSessionId = openSession(drm); 1405 try { 1406 if (!preparePlayback( 1407 MIME_VIDEO_AVC, 1408 new String[0], 1409 CENC_AUDIO_URL, false /* audioEncrypted */ , 1410 CENC_VIDEO_URL, true /* videoEncrypted */, 1411 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false /* scrambled */, 1412 mSessionId, getSurfaces())) { 1413 closeSession(drm, mSessionId); 1414 stopDrm(drm); 1415 return; 1416 } 1417 } catch (Exception e) { 1418 throw new Error("Unexpected exception ", e); 1419 } 1420 1421 // clear the expiration update listener 1422 drm.clearOnExpirationUpdateListener(); 1423 mDrmInitData = mMediaCodecPlayer.getDrmInitData(); 1424 getKeys(drm, initDataType, mSessionId, mDrmInitData, 1425 keyType, clearKeyIds); 1426 1427 // wait for the event, it should not arrive 1428 // because the expiration update listener has been cleared 1429 try { 1430 closeSession(drm, mSessionId); 1431 // wait up to 2 seconds for event 1432 for (int i = 0; i < 20 && !mExpirationUpdateReceived; i++) { 1433 try { 1434 Thread.sleep(100); 1435 } catch (InterruptedException e) { 1436 } 1437 } 1438 if (mExpirationUpdateReceived) { 1439 throw new Error("onExpirationUpdateListener should not be called"); 1440 } 1441 } catch (MediaDrmStateException e) { 1442 throw new Error("Unexpected exception from closing session: ", e); 1443 } finally { 1444 stopDrm(drm); 1445 } 1446 } 1447 1448 /** 1449 * Test that after onClearEventListener is called, 1450 * MediaDrm's event listener is not called. 1451 * 1452 * Clearkey plugin's provideKeyResponse method sends a 1453 * vendor defined event to the media drm event listener 1454 * for testing purpose. Check that after onClearEventListener 1455 * is called, the event listener is not called. 1456 */ 1457 @Presubmit 1458 @FrameworkSpecificTest testClearOnEventListener()1459 public void testClearOnEventListener() { 1460 1461 if (watchHasNoClearkeySupport()) { 1462 return; 1463 } 1464 1465 MediaDrm drm = null; 1466 mSessionId = null; 1467 mEventListenerCalled = false; 1468 1469 // provideKeyResponse in clearkey plugin sends a 1470 // vendor defined event to test the event listener; 1471 // we therefore start a license request which will 1472 // call provideKeyResonpse 1473 byte[][] clearKeyIds = new byte[][] { CLEAR_KEY_CENC }; 1474 int keyType = MediaDrm.KEY_TYPE_STREAMING; 1475 String initDataType = new String("cenc"); 1476 1477 drm = startDrm(clearKeyIds, initDataType, CLEARKEY_SCHEME_UUID, keyType); 1478 mSessionId = openSession(drm); 1479 try { 1480 if (!preparePlayback( 1481 MIME_VIDEO_AVC, 1482 new String[0], 1483 CENC_AUDIO_URL, false /* audioEncrypted */ , 1484 CENC_VIDEO_URL, true /* videoEncrypted */, 1485 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false /* scrambled */, 1486 mSessionId, getSurfaces())) { 1487 closeSession(drm, mSessionId); 1488 stopDrm(drm); 1489 return; 1490 } 1491 } catch (Exception e) { 1492 throw new Error("Unexpected exception ", e); 1493 } 1494 1495 // test that the onEvent listener is called 1496 mDrmInitData = mMediaCodecPlayer.getDrmInitData(); 1497 getKeys(drm, initDataType, mSessionId, mDrmInitData, 1498 keyType, clearKeyIds); 1499 1500 // wait for the vendor defined event, it should not arrive 1501 // because the event listener is cleared 1502 try { 1503 // wait up to 2 seconds for event 1504 for (int i = 0; i < 20 && !mEventListenerCalled; i++) { 1505 try { 1506 Thread.sleep(100); 1507 } catch (InterruptedException e) { 1508 } 1509 } 1510 if (!mEventListenerCalled) { 1511 closeSession(drm, mSessionId); 1512 stopDrm(drm); 1513 throw new Error("onEventListener should be called"); 1514 } 1515 } catch (MediaDrmStateException e) { 1516 closeSession(drm, mSessionId); 1517 stopDrm(drm); 1518 throw new Error("Unexpected exception from closing session: ", e); 1519 } 1520 1521 // clear the drm event listener 1522 // and test that the onEvent listener is not called 1523 mEventListenerCalled = false; 1524 drm.clearOnEventListener(); 1525 getKeys(drm, initDataType, mSessionId, mDrmInitData, 1526 keyType, clearKeyIds); 1527 1528 // wait for the vendor defined event, it should not arrive 1529 // because the event listener is cleared 1530 try { 1531 closeSession(drm, mSessionId); 1532 // wait up to 2 seconds for event 1533 for (int i = 0; i < 20 && !mEventListenerCalled; i++) { 1534 try { 1535 Thread.sleep(100); 1536 } catch (InterruptedException e) { 1537 } 1538 } 1539 if (mEventListenerCalled) { 1540 throw new Error("onEventListener should not be called"); 1541 } 1542 } catch (MediaDrmStateException e) { 1543 throw new Error("Unexpected exception from closing session: ", e); 1544 } finally { 1545 stopDrm(drm); 1546 } 1547 } 1548 1549 /** 1550 * Test that the framework handles a device returning invoking 1551 * the ::android::hardware::drm@1.2::sendSessionLostState callback 1552 * Expected behavior: OnSessionLostState is called with 1553 * the sessionId 1554 */ 1555 @Presubmit 1556 @FrameworkSpecificTest testSessionLostStateError()1557 public void testSessionLostStateError() { 1558 1559 if (watchHasNoClearkeySupport()) { 1560 return; 1561 } 1562 1563 boolean gotException = false; 1564 mLostStateReceived = false; 1565 1566 MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC }, "cenc", 1567 CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_STREAMING); 1568 1569 mDrm.setPropertyString("drmErrorTest", "lostState"); 1570 mSessionId = openSession(drm); 1571 1572 // simulates session lost state here, detected by closeSession 1573 1574 try { 1575 try { 1576 closeSession(drm, mSessionId); 1577 } catch (MediaDrmStateException e) { 1578 gotException = true; // expected for lost state 1579 } 1580 // wait up to 2 seconds for event 1581 for (int i = 0; i < 20 && !mLostStateReceived; i++) { 1582 try { 1583 Thread.sleep(100); 1584 } catch (InterruptedException e) { 1585 } 1586 } 1587 if (!mLostStateReceived) { 1588 throw new Error("Callback for OnSessionLostStateListener not received"); 1589 } 1590 } catch(Exception e) { 1591 throw new Error("Unexpected exception ", e); 1592 } finally { 1593 stopDrm(drm); 1594 } 1595 if (!gotException) { 1596 throw new Error("Didn't receive expected MediaDrmStateException"); 1597 } 1598 } 1599 1600 /** 1601 * Test that the framework handles a device ignoring 1602 * events for the onSessionLostStateListener after 1603 * clearOnSessionLostStateListener is called. 1604 * 1605 * Expected behavior: OnSessionLostState is not called with 1606 * the sessionId 1607 */ 1608 @Presubmit 1609 @FrameworkSpecificTest testClearOnSessionLostStateListener()1610 public void testClearOnSessionLostStateListener() { 1611 1612 if (watchHasNoClearkeySupport()) { 1613 return; 1614 } 1615 1616 boolean gotException = false; 1617 mLostStateReceived = false; 1618 1619 MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC }, "cenc", 1620 CLEARKEY_SCHEME_UUID, MediaDrm.KEY_TYPE_STREAMING); 1621 1622 mDrm.setPropertyString("drmErrorTest", "lostState"); 1623 mSessionId = openSession(drm); 1624 1625 // Simulates session lost state here, event is sent from closeSession. 1626 // The session lost state should not arrive in the listener 1627 // after clearOnSessionLostStateListener() is called. 1628 try { 1629 try { 1630 mDrm.clearOnSessionLostStateListener(); 1631 Thread.sleep(2000); 1632 closeSession(drm, mSessionId); 1633 } catch (MediaDrmStateException e) { 1634 gotException = true; // expected for lost state 1635 } 1636 // wait up to 2 seconds for event 1637 for (int i = 0; i < 20 && !mLostStateReceived; i++) { 1638 try { 1639 Thread.sleep(100); 1640 } catch (InterruptedException e) { 1641 } 1642 } 1643 if (mLostStateReceived) { 1644 throw new Error("Should not receive callback for OnSessionLostStateListener"); 1645 } 1646 } catch(Exception e) { 1647 throw new Error("Unexpected exception ", e); 1648 } finally { 1649 stopDrm(drm); 1650 } 1651 if (!gotException) { 1652 throw new Error("Didn't receive expected MediaDrmStateException"); 1653 } 1654 } 1655 1656 @Presubmit 1657 @FrameworkSpecificTest testIsCryptoSchemeSupportedWithSecurityLevel()1658 public void testIsCryptoSchemeSupportedWithSecurityLevel() { 1659 if (watchHasNoClearkeySupport()) { 1660 return; 1661 } 1662 1663 if (MediaDrm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID, "cenc", 1664 MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL)) { 1665 throw new Error("Clearkey claims to support SECURITY_LEVEL_HW_SECURE_ALL"); 1666 } 1667 if (!MediaDrm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID, "cenc", 1668 MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO)) { 1669 throw new Error("Clearkey claims not to support SECURITY_LEVEL_SW_SECURE_CRYPTO"); 1670 } 1671 } 1672 1673 @Presubmit 1674 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) 1675 @FrameworkSpecificTest testMediaDrmStateExceptionErrorCode()1676 public void testMediaDrmStateExceptionErrorCode() 1677 throws ResourceBusyException, UnsupportedSchemeException, NotProvisionedException { 1678 if (watchHasNoClearkeySupport()) { 1679 return; 1680 } 1681 1682 MediaDrm drm = null; 1683 try { 1684 drm = new MediaDrm(CLEARKEY_SCHEME_UUID); 1685 byte[] sessionId = drm.openSession(); 1686 drm.closeSession(sessionId); 1687 1688 byte[] ignoredInitData = new byte[]{1}; 1689 drm.getKeyRequest(sessionId, ignoredInitData, "cenc", 1690 MediaDrm.KEY_TYPE_STREAMING, 1691 null); 1692 } catch(MediaDrmStateException e) { 1693 Log.i(TAG, "Verifying exception error code", e); 1694 assertFalse("ERROR_SESSION_NOT_OPENED requires new session", e.isTransient()); 1695 assertEquals("Expected ERROR_SESSION_NOT_OPENED", 1696 MediaDrm.ErrorCodes.ERROR_SESSION_NOT_OPENED, e.getErrorCode()); 1697 assertTrue("Expected ERROR_SESSION_NOT_OPENED value in info", 1698 e.getDiagnosticInfo().contains( 1699 String.valueOf(MediaDrm.ErrorCodes.ERROR_SESSION_NOT_OPENED))); 1700 } finally { 1701 if (drm != null) { 1702 drm.close(); 1703 } 1704 } 1705 } 1706 1707 /** 1708 * The test tries to enforce the behavior described 1709 * in {@link android.media.MediaDrm.KeyRequest#getDefaultUrl()}. 1710 * It should return an empty string if the default URL is not known. 1711 */ 1712 @Presubmit 1713 @ApiTest(apis = {"android.media.MediaDrm.KeyRequest#getDefaultUrl"}) 1714 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1715 @FrameworkSpecificTest testGetKeyRequestDefaultUrl()1716 public void testGetKeyRequestDefaultUrl() 1717 throws UnsupportedSchemeException, NotProvisionedException { 1718 if (watchHasNoClearkeySupport() || !FIRST_RELEASE_IS_AT_LEAST_U || !VNDK_IS_AT_LEAST_U) { 1719 return; 1720 } 1721 1722 MediaDrm drm = new MediaDrm(CLEARKEY_SCHEME_UUID); 1723 byte[] sessionId = openSession(drm); 1724 1725 try { 1726 if (!preparePlayback( 1727 MIME_VIDEO_AVC, 1728 new String[0], 1729 CENC_AUDIO_URL, false /* audioEncrypted */ , 1730 CENC_VIDEO_URL, true /* videoEncrypted */, 1731 VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false /* scrambled */, 1732 sessionId, getSurfaces())) { 1733 closeSession(drm, sessionId); 1734 stopDrm(drm); 1735 return; 1736 } 1737 } catch (Exception e) { 1738 throw new Error("Unexpected exception ", e); 1739 } 1740 1741 MediaDrm.KeyRequest drmRequest = drm.getKeyRequest(sessionId, mMediaCodecPlayer.getDrmInitData(), "cenc", 1742 MediaDrm.KEY_TYPE_STREAMING, 1743 null); 1744 String defaultUrl = drmRequest.getDefaultUrl(); 1745 Log.i(TAG, "Default url is [" + defaultUrl + "]."); 1746 assertEquals("Default url of key request should be empty", "", defaultUrl); 1747 drm.close(); 1748 } 1749 testIntegerProperties(MediaDrm drm, String testKey)1750 private void testIntegerProperties(MediaDrm drm, String testKey) 1751 throws ResourceBusyException, UnsupportedSchemeException, NotProvisionedException { 1752 if (getClearkeyVersionInt(drm) < 14) { 1753 return; 1754 } 1755 String testValue = "123456"; 1756 assertEquals("Default value not 0", drm.getPropertyString(testKey), "0"); 1757 Assert.assertThrows("Non-numeric must throw", Exception.class, () -> { 1758 drm.setPropertyString(testKey, "xyz"); }); 1759 Assert.assertThrows("Non-integral must throw", Exception.class, () -> { 1760 drm.setPropertyString(testKey, "3.141"); }); 1761 Assert.assertThrows("Out-of-range (MAX) must throw", Exception.class, () -> { 1762 drm.setPropertyString(testKey, Long.toString(Long.MAX_VALUE)); }); 1763 Assert.assertThrows("Out-of-range (MIN) must throw", Exception.class, () -> { 1764 drm.setPropertyString(testKey, Long.toString(Long.MIN_VALUE)); }); 1765 drm.setPropertyString(testKey, testValue); 1766 assertEquals("Property didn't match", drm.getPropertyString(testKey), testValue); 1767 } 1768 getClearkeyVersion(MediaDrm drm)1769 private String getClearkeyVersion(MediaDrm drm) { 1770 try { 1771 return drm.getPropertyString("version"); 1772 } catch (Exception e) { 1773 return "unavailable"; 1774 } 1775 } 1776 getClearkeyVersionInt(MediaDrm drm)1777 private int getClearkeyVersionInt(MediaDrm drm) { 1778 try { 1779 return Integer.parseInt(drm.getPropertyString("version")); 1780 } catch (Exception e) { 1781 return Integer.MIN_VALUE; 1782 } 1783 } 1784 cannotHandleGetPropertyByteArray(MediaDrm drm)1785 private boolean cannotHandleGetPropertyByteArray(MediaDrm drm) { 1786 boolean apiNotSupported = false; 1787 byte[] bytes = new byte[0]; 1788 try { 1789 bytes = drm.getPropertyByteArray(DEVICEID_PROPERTY_KEY); 1790 } catch (IllegalArgumentException e) { 1791 // Expected exception for invalid key or api not implemented 1792 apiNotSupported = true; 1793 } 1794 return apiNotSupported; 1795 } 1796 cannotHandleSetPropertyString(MediaDrm drm)1797 private boolean cannotHandleSetPropertyString(MediaDrm drm) { 1798 boolean apiNotSupported = false; 1799 final byte[] bytes = new byte[0]; 1800 try { 1801 drm.setPropertyString(LISTENER_TEST_SUPPORT_PROPERTY_KEY, "testing"); 1802 } catch (IllegalArgumentException e) { 1803 // Expected exception for invalid key or api not implemented 1804 apiNotSupported = true; 1805 } 1806 return apiNotSupported; 1807 } 1808 watchHasNoClearkeySupport()1809 private boolean watchHasNoClearkeySupport() { 1810 if (!MediaDrm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) { 1811 if (isWatchDevice()) { 1812 return true; 1813 } else { 1814 throw new Error(ERR_MSG_CRYPTO_SCHEME_NOT_SUPPORTED); 1815 } 1816 } 1817 return false; 1818 } 1819 getSurfaces()1820 private List<Surface> getSurfaces() { 1821 return Arrays.asList(getActivity().getSurfaceHolder().getSurface()); 1822 } 1823 } 1824