//////////////////////////////////////////////////////////////////////////////// //// Copyright 2021 Google LLC //// //// This software is licensed under the terms defined in the Widevine Master //// License Agreement. For a copy of this agreement, please contact //// widevine-licensing@google.com. //////////////////////////////////////////////////////////////////////////////// package com.google.video.widevine.sdk.wvpl; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; /** Tests for {@link WvPLProxySession} Java API in Widevine Proxy Server SDK. */ public class WvPLProxyUsingDspExample { /* Creates WvTestRunner to run wvpl proxy test */ public static String loadCertificateStatusListFromFile(String certificateStatusListFile) throws IOException { BufferedReader br = Files.newBufferedReader(Path.of(certificateStatusListFile), UTF_8); StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { sb.append(line); sb.append(System.lineSeparator()); line = br.readLine(); } br.close(); return sb.toString(); } static class WvTestRunner extends Thread { private final int loops; private final String threadName; private final WvPLProxyEnvironment env; private List customProfileOwners; private String contentOwner; public WvTestRunner(WvPLProxyEnvironment env, ThreadGroup group, String threadName, int loops) { super(group, threadName); this.loops = loops; this.threadName = threadName; this.env = env; } public void setCustomProfileOwners(List customProfileOwners) { this.customProfileOwners = customProfileOwners; } public void setContentOwner(String contentOwner) { this.contentOwner = contentOwner; } @Override public void run() { System.out.println("Thread Started: " + threadName); try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println("Unexpected error when starting thread = " + threadName); } String testSessionId = "TestSessionId"; String testPurchaseId = "TestPurchaseId"; String testProviderClientToken = "TestProviderClientToken"; byte[] testMasterSigningKeys = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; boolean testOverrideProviderClientToken = true; String b64LicenseRequest = "CAcSuQsKhAsIARLtCQquAggCEhCLiDL7eSGz5lQZxhEvltHOGOTt1OEFIo4CMIIBCgKCAQEApynnDkNRVwiBn-bGL5_C" + "bqnyMAvVzKeRMbRpl8woIqhHmljqKHt9zq2mt3KQzQ3z96mL1OkZB2Mb_EBlr-k7CPlaV5n3ORJS3mo9Cg5QLc" + "EWwOCFPIdeXr_4Q4WMMi8sBgF9QjmBp6QQcP-OrgwqwTVJEYfrInkHsRhey3bNPybrAlmj5sF3w1reLAeqJD0p" + "WZ4lGmq027m-oAESe9OXZb1AobsjuIOxCrgprd4XMRuQKVBZVwk28O5exr2roL4Hrb_zBQVTfGfxAE2vRp5tdg" + "Ak9vBwlWh_3jWmJlG1BL7qynkoLOgwAakU_dnvrOfm0VlZq-5TazxMtT5XOzkSjQIDAQABKJkgEoACMjGvs7ZD" + "6n2zCV_co_6N9J4pnSsZ_p2nq18obvIXoIImvQ1TDpaD-7eO3syn2Lw_H5rAOlOo1IApwPSQpq8wG9onMW5ZIE" + "CaWoiay6SVOdHZLAt4SxgdoRTzu0XtvHu6fN36blmVAOkvjA3RHs6-u4D-xzJz1cYgvzCzDAvhqQ8bhuqRNJc5" + "2WG1ic4bG4PTHhNeSJt1KjJDGtG9botphtvQyYBzhytTSRH8jr-lKMYvaWm21LUHmmQLAvpZiczKxwjUEw5Ghg" + "IPi1D56jj4JCQpNVX4L8kgrTP0E5i1Mv4B4otQ9201NJg1MocUlpurZ_aOs8wwqsXScf_z4bHJYhq2BQqwAggB" + "EhAITWC8ZZN9tqx_mStB7PXyGKLTiowFIo4CMIIBCgKCAQEAmRs1e7cU3srB5w6qNtHQg03ySj0QNp-Qoic0o6" + "1K68z8JDK91tzR_9dWmbpWCMRFeXLSfEerz_jIjNmQCTgmC4yFeVMnsq7zodsn6LliA5KE1gLGp0xqFDpnvmSh" + "ahyPRPr62sJCzrbYrenE1FQuvT7p0qCnHrekzPkW4C8meTsME6SSHD7tScXJbNRVWY7eBAzkcrAVqa882oLEl5" + "Kqf2u_v0ZnKTtE7HQpw5oDiRHOH-Riw16VwmTTYzQ8OtuUT6KqvboPthxAJ5dYXz37eW9s4dzzt5yYh76YFHDs" + "vLBI5Z5WT8OtLEuGD3CqRo-2bEooR-7HVa3om9YiV0DJpQIDAQABKJkgMAESgAMhLeFBwTBXwZhyGN1_vC3J-E" + "yC-R9bFj0DcMcGk4fHDJDDBg8FBFc8bgvTrLmE_Dq3siGnfTczMk1nCppe6mRpHVZmqbZ902eAPNV1O9depyOb" + "7AwAgpdpV3HOIDxgiY1PJkrVXT0ByW9m1vtsU9GSimCtBA19SOnIPWhJwkjkXA-Vu7jNCx_u5EWMXGCrDVLh-w" + "-O6Ydmr5z5MwdXhlfoAPGk7T9qAOiVGyFY9Fz8wvfl5cw9fwTqK-NKqN1J69eMYYwUBOMIJX5QKqyN8A1SqJhT" + "Ab_l5ueE13sLxSyTWosYP7Wv7SWlhVp3_PJxXJB2EWJKWLA32BmFe6uSa-5NVgJ3CJ77Bw-z9ThdHd4eSG2PMt" + "JT3oaS4wmONbMZJkfnlUTPD7KLNiEz6UKXeBzz9olgUxZn7nTnAmvXITfrHd0FeDTPHavo1wafiKy0t6QCRw8o" + "ASZLtJw7_uJPxkCkY3TWwQvH0rlgeS0R2lCGq4zP9GGAwkPDldIvGp5PawIaEwoMY29tcGFueV9uYW1lEgN3d3" + "caEQoKbW9kZWxfbmFtZRIDd3d3GiAKDHByb2R1Y3RfbmFtZRIQV2lkZXZpbmVDYXNUZXN0cxomChFhcmNoaXRl" + "Y3R1cmVfbmFtZRIRYXJjaGl0ZWN0dXJlX25hbWUaGgoLZGV2aWNlX25hbWUSC2RldmljZV9uYW1lMgIoDhIgCh" + "4KHBoNd2lkZXZpbmVfdGVzdCIJQ2FzVHNGYWtlWAIYASDk7dThBTAVONOi-_4CGoACBXDXgJopfGQLxWI0z-o2" + "h-dH3L-ObMMm3moRAv7MQ-xnTALlyNMRCaGsfyy1L0SmsrQQptJjK7hB6tZDe-UQ76fIpQLeHnNOBXuaxl7sY2" + "Gvrztx8HT_N0KFgAZ9hW1L--zvFw0R1ctjG88xGTpmFWsGoWEZ0NGTgc1XyssK62tuWz5CfIEXvhN7ZiMyoagP" + "SOWZZ1Mltbiv1AEPeFC6My-njq8N3u9whokMzhlGMePSBctFmn4f8RtUeR7zlLdPuKcp0BBLAOF7ngXF1C-skj" + "-Y1DrtxwqwczA56yLzXGlh5FhwhaFMSprOHbAFCIQ-ee66aDIbVZ-9_fBMiIE83Q=="; byte[] decodedLicenseRequest = Base64.getUrlDecoder().decode(b64LicenseRequest); for (int i = 0; i < loops; i++) { try { byte[] bad = {1, 2, 3, 4}; env.createSession(bad); } catch (WvPLStatusException e) { System.out.println("Message for bad session: " + e.getMessage()); System.out.println("Status message for bad session: " + e.getStatus().getMessage()); } try { byte[] serviceCertificateRequest = Base64.getUrlDecoder().decode("CAQ="); env.createSession(serviceCertificateRequest); } catch (WvPLStatusException e) { System.out.println("Message for failed to create session with service certificate " + "request: " + e.getMessage()); assertEquals(WvPLStatus.StatusCode.SERVICE_CERTIFICATE_REQUEST_MESSAGE, e.getStatus().getStatusCode()); try { byte[] serviceCert = env.generateDrmServiceCertificateResponse(); System.out.println("Returning service certificate: " + Base64.getUrlEncoder().encodeToString(serviceCert)); } catch (WvPLStatusException wvplE) { System.out.println("Failed to get the DRM Service Certifidate: " + wvplE.getStatus().getMessage()); } } WvPLProxySession session = null; try { session = env.createSession(decodedLicenseRequest); } catch (WvPLStatusException e) { System.out.println("Message: " + e.getMessage()); System.out.println("Status message: " + e.getStatus().getMessage()); } System.out.println("Version: " + WvPLProxySession.getVersionString()); WvPLRequestType requestType = session.getRequestType(); System.out.println("Message type : " + requestType.getMessageType()); if (requestType.getMessageType() != WvPLMessageType.MessageType.LICENSE_REQUEST) { System.out.println( "Expected LICENSE_REQUEST, Unexpected message type : " + requestType.getMessageType()); System.exit(3); } System.out.println("License type : " + requestType.getLicenseType()); System.out.println("LicenseRequest type : " + requestType.getLicenseRequestType()); WvPLStatus status; try { WvPLClientInfo clientInfo = session.getClientInfo(); WvPLHdcp.HDCP maxHdcpVersion = clientInfo.getMaxHdcpVersion(); System.out.println("max hdcp version = " + maxHdcpVersion.getHDCP() + ", oem crypto api version = " + clientInfo.getOemCryptoApiVersion() + ", provider client token = " + clientInfo.getProviderClientToken()); Map namesValues = clientInfo.getNamesValues(); for (Map.Entry nameValue : namesValues.entrySet()) { System.out.println("Key = " + nameValue.getKey() + ", Value = " + nameValue.getValue()); } } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println("GetClientInfo WvPLStatus: code = " + status.getStatusCode() + ", message = " + status.getMessage()); } // Set parameters on the created session. // Validate setting/getting WvPLPlaybackPolicy for a session WvPLPlaybackPolicy policy = new WvPLPlaybackPolicy(); policy.setLicenseDurationSeconds(10000000L); session.setPolicy(policy); assertEquals( policy.getLicenseDurationSeconds(), session.getPolicy().getLicenseDurationSeconds()); // Optional. Check the qualified custom profiles for the content owner. List qualifiedProfileNames; if (customProfileOwners.contains(contentOwner)) { try { qualifiedProfileNames = session.getQualifiedCustomDeviceSecurityProfiles(contentOwner); if (!qualifiedProfileNames.isEmpty()) { System.out.println( "DRM qualifying custom profiles for owner <" + contentOwner + ">:"); for (String profileName : qualifiedProfileNames) { System.out.printf("%s ", profileName); } System.out.println(); } } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println( "Failed to get qualifying custom profiles: " + status.getStatusCode() + ", message: " + status.getMessage() + " for owner: <" + contentOwner + ">"); } } // Content keys are generated based on the info from qualifying profiles. // The qualified profiles can be used to determine the content keys allowed in the license. // In this example, the following `key1` and `key2` are generated based on qualified custom // profiles. Those keys would support SD and HD content. // Adding a TrackType.VIDEO_SD key (without keyId) WvPLKey key1 = new WvPLKey(); byte[] data1 = {10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; key1.setKeyBytes(data1); key1.setTrackType(WvPLTrackType.TrackType.VIDEO_SD); status = session.addKey(key1); assertEquals(WvPLStatus.StatusCode.OK, status.getStatusCode()); // Adding a TrackType.VIDEO_HD key WvPLKey key2 = new WvPLKey(); byte[] data2 = {10, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; key2.setKeyId(data2); key2.setKeyBytes(data2); key2.setTrackType(WvPLTrackType.TrackType.VIDEO_HD); // Validate setting/getting WvPLOutputProtection values WvPLOutputProtection outputProtection = new WvPLOutputProtection(); outputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_V2_2); assertEquals(WvPLHdcp.HDCP.HDCP_V2_2, outputProtection.getHdcp()); outputProtection.setSecurityLevel(WvPLSecurityLevel.SecurityLevel.SW_SECURE_DECODE); assertEquals( WvPLSecurityLevel.SecurityLevel.SW_SECURE_DECODE, outputProtection.getSecurityLevel()); key2.setOutputProtection(outputProtection); // Validate setting/getting WvPLVideoResolutionConstraints WvPLVideoResolutionConstraint videoResolutionConstraint1 = new WvPLVideoResolutionConstraint(); videoResolutionConstraint1.setMinResolutionPixels(300); videoResolutionConstraint1.setMaxResolutionPixels(600); videoResolutionConstraint1.setHdcp(WvPLHdcp.HDCP.HDCP_V2); WvPLVideoResolutionConstraint videoResolutionConstraint2 = new WvPLVideoResolutionConstraint(); videoResolutionConstraint2.setMinResolutionPixels(3000); videoResolutionConstraint2.setMaxResolutionPixels(6000); videoResolutionConstraint2.setHdcp(WvPLHdcp.HDCP.HDCP_V1); // Validate adding constraints and output protection values to key2 (VIDEO_HD) key2.addVideoResolutionConstraint(videoResolutionConstraint1); key2.addVideoResolutionConstraint(videoResolutionConstraint2); assertThat(key2.getVideoResolutionConstraint()).hasSize(2); for (WvPLVideoResolutionConstraint vrc : key2.getVideoResolutionConstraint()) { if (vrc.getHdcp() == WvPLHdcp.HDCP.HDCP_V2) { assertEquals(videoResolutionConstraint1, vrc); } else { assertEquals(videoResolutionConstraint2, vrc); } } WvPLOutputProtection requestedOutputProtection = new WvPLOutputProtection(); requestedOutputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_V2); key2.setRequestedOutputProtection(requestedOutputProtection); status = session.addKey(key2); assertEquals(WvPLStatus.StatusCode.OK, status.getStatusCode()); // Or we could call filterKey() to filter out the keys. Keys could be inserted // with one security profile as required profile, as well as content owner. In // filterkey() API it already contains the logic of checking the qualified security // profiles for the content owner. WvPLKey key3 = new WvPLKey(); byte[] data3 = {4, 4, 4, 4, 4, 4, 4, 4, 4, 10, 11, 12, 13, 14, 15, 16}; key3.setKeyBytes(data3); key3.setTrackType(WvPLTrackType.TrackType.VIDEO_HD); outputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_NONE); outputProtection.setSecuredataPath(true); key3.setOutputProtection(outputProtection); String requiredProfileName = "dsp_test"; key3.setRequiredProfile(requiredProfileName.getBytes(UTF_8)); key3.setContentOwner(contentOwner.getBytes(UTF_8)); WvPLCapabilityStatus capabilityStatus = session.filterKey(key3); assertEquals( WvPLCapabilityStatus.DeviceCapabilityStatus.CAPABILITY_OK, capabilityStatus.getStatus()); // The following example `wvpl_key4` will be filtered out because the profile name the key // associated with is not qualified. WvPLKey key4 = new WvPLKey(); byte[] data4 = {6, 6, 6, 6, 6, 6, 6, 6, 6, 10, 11, 12, 13, 14, 15, 16}; key4.setKeyBytes(data4); key3.setTrackType(WvPLTrackType.TrackType.VIDEO_HD); outputProtection.setHdcp(WvPLHdcp.HDCP.HDCP_NONE); outputProtection.setSecuredataPath(true); key3.setOutputProtection(outputProtection); requiredProfileName = "L3"; key3.setRequiredProfile(requiredProfileName.getBytes(UTF_8)); key3.setContentOwner(contentOwner.getBytes(UTF_8)); capabilityStatus = session.filterKey(key4); assertEquals( WvPLCapabilityStatus.DeviceCapabilityStatus.CAPABILITY_UNQUALIFIED_PROFILE, capabilityStatus.getStatus()); // Validate setting/getting sessionInit values WvPLSessionInit sessionInit = new WvPLSessionInit(); sessionInit.setSessionId(testSessionId); sessionInit.setPurchaseId(testPurchaseId); sessionInit.setMasterSigningKey(testMasterSigningKeys); sessionInit.setProviderClientToken(testProviderClientToken); sessionInit.setOverrideProviderClientToken(testOverrideProviderClientToken); session.setSessionInit(sessionInit); assertEquals(testSessionId, session.getSessionInit().getSessionId()); assertEquals(testPurchaseId, session.getSessionInit().getPurchaseId()); assertEquals(Arrays.toString(testMasterSigningKeys), Arrays.toString(session.getSessionInit().getMasterSigningKey())); assertEquals(testProviderClientToken, session.getSessionInit().getProviderClientToken()); assertEquals(testOverrideProviderClientToken, session.getSessionInit().getOverrideProviderClientToken()); // Test getDeviceInfo() function. try { WvPLDeviceInfo deviceInfo = session.getDeviceInfo(); System.out.println("soc = " + deviceInfo.getSoc() + ", manufacturer = " + deviceInfo.getManufacturer() + ", model = " + deviceInfo.getModel() + ", device_type = " + deviceInfo.getDeviceType() + ", system_id = " + deviceInfo.getSystemId()); } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println("GetDeviceInfo WvPLStatus: code = " + status.getStatusCode() + ", message = " + status.getMessage()); } try { WvPLWidevinePsshData widevinePsshData = session.getPsshData(); if (!widevinePsshData.getKeyIds().isEmpty()) { for (byte[] keyId : widevinePsshData.getKeyIds()) { System.out.println("keyId from Widevine Pssh = " + Arrays.toString(keyId)); } System.out.println("Content Id" + Arrays.toString(widevinePsshData.getContentId())); } else { System.out.println("KeyIds from Widevine Pssh is empty"); } } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println("Get WvPLStatus: code = " + status.getStatusCode() + ", message = " + status.getMessage()); } // Test generateEncodedLicenseRequest() function. // TODO(yawenyu): Check if licenseRequest can be base64 decoded and parsed into the proto. try { String licenseRequest = session.generateLicenseRequest(); if (licenseRequest.length() == 0) { System.out.println("Base64 encoded license request is empty"); System.exit(-1); } System.out.println("License request is : " + licenseRequest); } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println("GenerateEncodedLicenseReques failed. WvPLStatus: code = " + status.getStatusCode() + ", message = " + status.getMessage()); } // Test getResponseStatus. String input = "{\"status\":\"OK\",\"license\":\"CAISdgpKCiBBOEVEMDBBQUVEODM2RThFODMwNDAwMDAwMDAwMDAwMBIgQThFRDAwQUFFRDgzNkU4RTgzMDQwMDAwMDAwMDAwMDAaACABKAQSBggBEAEYARoWIANCEgoQa2MxMwAAAAAuNBxJAAAACCDe1eziBSgBUAMaIDn9npcVDpyEU7RpVHPvVCAmlTDVEbaBDUdi+LWCCWBL\"}"; try { String responseStatus = session.getResponseStatus(input); assertEquals("OK", responseStatus); } catch (WvPLStatusException e) { System.out.println("Failed to getResponseStatus."); } try { session.getLicense(input); } catch (WvPLStatusException e) { System.out.println("getResponseStatus status = " + status.getStatusCode() + ", message = " + status.getMessage()); } try { WvPLClientCapabilities wvplClientCapabilities = session.getClientCapabilities(); System.out.println("WvPLClientCapabilities = " + wvplClientCapabilities); } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println("GetClientInfo WvPLStatus: code = " + status.getStatusCode() + ", message = " + status.getMessage()); } try { env.destroySession(session); } catch (IOException e) { System.out.println("IOException when closing: " + e.getMessage()); } // TODO(yawenyu): Add more test cases for getResponseStatus. } System.out.println("Thread Ended: " + threadName); } } public static void main(String[] argv) throws Exception { int numberOfRunners = 10; int numberOfLoops = 100; String b64DrmServiceCertificate = "CscCCAMSEGMj4kXUq6vl6Zu3yX-iATQY8YHR4wUijgIwggEKAoIBAQCQ4zD-Na1NJ-_R5EukD3ZwnkJUVijD5Bs70gBbvYrskXADVMSC-YnFgN5_lQ7Y1jqG1Vo_o2MBLNu4wCoGjD9Vfnw2uOVs5lccs5hKGgxqHPgKit3PvVJYe0fXRi571pE-bI9FKsKlt02VkC0OopgqCui-494J9mVvTfmkwg24F0BBn6JnzNMKKLYXqNmEVyL-xF5AprYbYQwQ2RsYx0xHJ5ggh5J9LOJHuCfePTykRmdvkJfh0bnoBRrcR9VJzU--6ZMwFhsD3SEmGnyjfh9_rD1De01xdMIU1tumBDR7CsFmWFB-s8GPFEy14lkG7LWRZYOUagSf2FPh2V8O3rK3AgMBAAE6GHdpZGV2aW5lLXRlc3QuZ29vZ2xlLmNvbUACEoADqy10oin03kb2QW1t7OiNAx9Y5leFlqUaBveD93qtip5BC-4TPmB2CqKEGr_uUoR7YSOfoSsc8XJy5aGK6s05ci-LNxcQsHMbZr6nieDVO56AQhk6KvP2tB9qvPBKxtg7FKpxDjF43Fv2IeVUHnTy8JzuToOHLwgGDhT90RYnEqCTpVcssalc7XF8iVNERyMCakuud4TH8EL_bCqmoVY4Emdqdiu1uSMsTSQuSGu5GeFlK3FXxEjmKCw2ItKCiB3z6HGwMB0VpvX-CCQThc46O2D8b4fnN529cBOAmqeBI7ELZXJjybjD9EiUXrLx6IKLiCpjp-aWiwsRK1hZN6yEH-cRfSiXX_8mON4qi-aUkdsyVlNSn4WNp5GpOoY5MSfTq4EisngTSBzoo_UgXMoS0xf-DOsj614TngSDgXRNPrKnf19K8KetqFTv7BTxLUQbFuNHqHWcqSnimmo4lIsYdDA4Ed76kRERtbMnqeyI__UNSbY0eEZ3uxO95OxZMgye"; String b64PrivateKey = "MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIGRMrX1uPR0sCAggAMB0GCWCGSAFlAwQBKgQQ8yhCy0bstsUeTLwl7saOrwSCBNDLB7oZx7Ypfodc9OR5aolJ6yMmKcvrB0jxRGioPLsT5flz_kWe4JMp25xR1c8Y0_ZrugU12zBbw9siyvfXhenrW0JVzxoqNGysggA_p-ZLXMxeQKcGwXHemWRMxlvO7wZG7gPANQhhkPnHzEN2kevvmsVTWIT7JVYGFALDg2mYgNsRslkblkfY51HvFS8vMOkL3YOG72NHnmpV4jMpiz9m7qiOcJ39oG2IldUb57e39LfnMO5WWafgrak2DEwL9zH6T3dxETcLK9hX19A_JDmsSUZk1EDyMh_JVIcjeDJASko3obeLavPLDZky_Nrl6tuht7vKhv0f_2PKxTCzJWCA7dtzNUymhszcxwRecV76lDMn6OcQ6DJblae423fJ9iDBOIHLFUDMfDoQDkRgFTAr4AagXurjipwRGu059khs4p60dHtGgkh0Rk5JuGv4pha4lUKtBP60ENFMV8MXc5na9p4rHVdRU7mQ2uetKTQN6uVInGXFhBH2oyPvxzTWcqh-xgOBdDUH5cWOEDdvvKFuNAj-URND8ypQVbnJ-mOBv3hdNil_ZQfOxf9MEWksT9ApoM9g-zAblIrh9wRavz_NlRy8AarSnxTLmlvxsNPsd_lPmLce0HZSdyGrUYgKBjWCabKPN-bA9o2FINdFSsztJrmUeTsFLJqujSB5oMjMeotmXKAkZgbwjEG-Agi5a4UMPw8Krl1dUlGxC42tVLnaucrWRnlKiqfQc4rncgUJ05cDUr8n2KY1RqfQGk-EuTiiiLA8eU_HhDSqPazpDT_yL6ZUgbswLVhd52_bK0vCXX4NsUsa3jF31Qq5vE56mnr7qrKpwzHuHxrIFiXiYtaH_dnhzasuOpCJbTZPRAebjWWf2eXpikJN4jA_J4q2smvENR82D9laATxLHzF41baHfvCPYjchNGA08P183-_YBaYGiJvnaNcnL_H7FDOT99V_gcwk1MixDy7AaRsiUG8ZuJQEEPi8J03GpSX8UTy9yDXRzXrgsuJZ5REAh5_bCu8OgI-y_RJ9bPvSKRere4VIdLj6YvVN1L68C1R-Z6HhHTQ6H4vaTLsYskGXN4N6wR-he6RYPq0EhCHRg0zNEVxRAqOxmwdUZKQ7IetqMT0geO30AwiUnCmJOIpku-lsCCGj8ECIIK198HN25mBnHcFWYyQbHGe73Z0rDjgWuiivvGC7pvsa6WjHwCtLVF8hTxal6ViGBN7-5zQ6YFZsmA5t8BloZBcohc5-jZUS62lgfyETHBmHVY8etQOm9L5dja7I18xlurZybwgUeMQQBZiwV3ynb0s_B1wQriAAdboFuglRD-dVqyobGMOk_ECePQ5LWQv6D5fqLaXzVyqLUJ_gpPQ6JAYbTywy5SIxx4JpIbqk17cBqaoYRP-RRA4DQGU_qrdh-HGqCesOL_yYqq2o7Gc-M-PWMlqYiEv8T2QbSywtWutqIkkok4Kqlo6zKn7Cgynhe0QsdXNVAlbNDzpdBTm6scKHl2PzLfuHOdwRe7BFkQAJJnMQuyrsGOMq4uEsKg0LMJm2hYOJ7PtMgFtIdXmdHQdsRkgkmHYZ5ryen-kjd7rIUtU1UjH2WPG3ROhdgMFw84uZGtt-WpsT4SrTrreC_QV_PlsOmqlHPBJg8w=="; String passphrase = "encryptallthekitties"; byte[] passphraseBytes = passphrase.getBytes(UTF_8); byte[] decodedDrmServiceCertificate = Base64.getUrlDecoder().decode(b64DrmServiceCertificate); byte[] decodedPrivateKey = Base64.getUrlDecoder().decode(b64PrivateKey); String providerIv = "d58ce954203b7c9a9a9d467f59839249"; String providerKey = "1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9"; String b64SerializedSignedDsps = "Cm0I45_P_" + "AUSLwoIZHNwX3Rlc3QQAhoECAAQACIGCAAQAyACKg13aWRldmluZV90ZXN0OgQIABAAEjQKC" + "GxpdmVkZW1vEAIaAggEIgIQASoNd2lkZXZpbmVfdGVzdDIFCNcIEAI6CAiDwrX6BRAAEoADb" + "oYJI5ouzml8W63DmiTBA7thzSl5OaKbyc1dAKGpHrMw-lTuUyrzULPm5nb_" + "st4YJk2w3F0k4Ly0lsRY_QweMSIechtcII5jO_9N2scy0y5V-lBRH_" + "7AtZFdfwD2wrseVCCarSyWDZN84-" + "6Wu3ekKbNO8vehiP8Tx2VWmkcIypJ09nPuTDxPT5CutLkgzlIS2q9HjwE5IK4nfC-" + "rx7seGgMxouyEb-QsTBtJ-Lx9O8D1GFMvOtHmfG-KfoD1L-" + "9lUT6j8W3TfyrUesnIDQy2bCCQzsbsNg60M9B-YZNvl6DX_-" + "PqE1CT2Lal7rp5TsZluOtwEj8TMBzyNl5kC5MAXZ3ETEFDag_9qEZrMJMIEsw194_lo_" + "o2ivjkiPNwCC00rJ91RI9gc2ctDlXUqtbqfz5-lx_v04VsI8AEYfuXmO8OBhFZwz4__" + "ZVwpI8fOtedm1KI46uCWZUwIi3yigYNJvgxoynzxVF8uMLTJi3iqiG5BAtrRImEt1iRVD8Ow" + "h4-GAI="; // Define the configuration that is to be used for WvPLEnvironment. Map configValues = new HashMap<>(); configValues.put("drm_certificate_type", "dev"); configValues.put("allow_unknown_device", "1"); configValues.put("provider", "widevine_test"); configValues.put("provider_iv", providerIv); configValues.put("provider_key", providerKey); // Set the device certificate expiration time to 10 years (10 * 365 * 24 * 3600). Note that in // practice, the expiration should not be 10 years long. Certificate status list should be // updated periodically. configValues.put("device_certificate_expiration", "315360000"); WvPLProxyEnvironment env = new WvPLProxyEnvironment(configValues); WvPLStatus status = env.setServiceCertificate( decodedDrmServiceCertificate, decodedPrivateKey, passphraseBytes); if (status.getStatusCode() != WvPLStatus.StatusCode.OK) { System.out.println("setServiceCertificate status = " + status.getStatusCode() + ", message = " + status.getMessage()); } // Get Service certificate response. try { byte[] serviceCertificateResponse = env.generateDrmServiceCertificateResponse(); System.out.println( "Service certificate response = " + Arrays.toString(serviceCertificateResponse)); } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println("getServiceCertificateResponse exception Message: " + e.getMessage() + ", exception status message: " + e.getStatus().getMessage()); } String certificateStatusListFile = "sdk/testing/sampleTestCertificateStatusList.json"; String certList = ""; try { certList = loadCertificateStatusListFromFile(certificateStatusListFile); } catch (FileNotFoundException e) { System.out.println("FileNotFoundException in reading input cert list."); } catch (IOException e) { System.out.println("IOException in reading input cert list."); } try { status = env.setDeviceCertificateStatusList(certList.getBytes(UTF_8)); } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println("setDeviceCertificateStatusList exception Message: " + e.getMessage() + ", exception status message: " + e.getStatus().getMessage() + ", numeric code = " + e.getStatus().getNumericCode()); } assertEquals(WvPLStatus.StatusCode.OK, status.getStatusCode()); System.out.println("Successful set DeviceCertificateStatusList in WvPLProxyEnvironment."); // Set custom device security profile list. // One time setup each time new DSPs are loaded. byte[] signedDsp = Base64.getUrlDecoder().decode(b64SerializedSignedDsps); status = env.setCustomDeviceSecurityProfiles(signedDsp); if (status.getStatusCode() != WvPLStatus.StatusCode.OK) { System.out.println("setCustomDeviceSecurityProfiles status = " + status.getStatusCode() + ", message = " + status.getMessage()); } // Optional sannity check. // Verify the custom profile owners match with list of expected content owners. List customProfileOwners = new ArrayList<>(); try { customProfileOwners = env.getCustomDeviceSecurityProfileOwners(); } catch (WvPLStatusException e) { status = e.getStatus(); System.out.println("getCustomDeviceSecurityProfileOwners exception Message: " + e.getMessage() + ", exception status message: " + e.getStatus().getMessage() + ", numeric code = " + e.getStatus().getNumericCode()); } if (customProfileOwners.isEmpty()) { System.out.println("Owner list is empty for custom device security profiles."); return; } System.out.printf("Custom profile owners are :"); for (String owner : customProfileOwners) { System.out.printf("%s, ", owner); } System.out.println(); String contentOwner = "widevine_test"; WvTestRunner[] runner = new WvTestRunner[numberOfRunners]; ThreadGroup group = new ThreadGroup("Test Runner"); for (int i = 0; i < runner.length; i++) { runner[i] = new WvTestRunner(env, group, "thread_runner_" + i, numberOfLoops); runner[i].setCustomProfileOwners(customProfileOwners); runner[i].setContentOwner(contentOwner); runner[i].start(); } for (WvPLProxyUsingDspExample.WvTestRunner element : runner) { element.join(); } env.close(); } }