Simple JavaScript PlayReady App Walkthrough - Part 2
The simple PlayReady protected Windows Store app uses service requests to individualize a device and to acquire a license that allows the app to play back PlayReady protected content.
Handling Service Requests
This section describes how set up service requests. The same code is used to handle license acquisition service requests, individualization service requests, domain join and domain leave service requests, and metering service requests. This sample, however, only uses individualization and license acquisition service requests.
To handle service requests
Add the following function to the end of the default.js file:
function serviceRequested(request) { try { var serviceRequest = request.request; logMsg("Service request required before playback can happen, service request type: " + getSRType(serviceRequest)); mpmCompletionNotifier = request.completion; g_mpmCompletionNotifier_set = true; g_PostSRCompleteFunction = function (hrResult) { if (g_mpmCompletionNotifier_set == true) { if (hrResult >= 0) { logMsg("mpmCompletionNotifier.complete( true )"); mpmCompletionNotifier.complete(true); g_mpmCompletionNotifier_set = false; } else { logMsg("mpmCompletionNotifier.complete( false )"); mpmCompletionNotifier.complete(false); } } }; HandleServiceRequest(serviceRequest); } catch (e) { logWarning(e, 'serviceRequested'); } }
This function is called when a service request occurs. The type of service request is included in the request parameter and is displayed in the log using the getSRType function. Next, g_PostSRCompletFunction is defined and is to be used when all service requests have completed. Then, if no exception has occurred, this function calls the HandleServiceRequest function to handle the service request.
Add the following function at the end of the file:
function getSRType(sr) { try { if (undefined == sr) { return '( Service Request Object Undefined )'; } else if (null == sr) { return '( Service Request Object NULL )'; } else if (Microsoft.Media.PlayReadyClient.PlayReadyStatics.licenseAcquirerServiceRequestType == sr.type) { return 'PlayReadyLicenseAcquirerServiceRequest'; } else if (Microsoft.Media.PlayReadyClient.PlayReadyStatics.individualizationServiceRequestType == sr.type) { return 'IndividualizationServiceRequest'; } else if (Microsoft.Media.PlayReadyClient.PlayReadyStatics.domainJoinServiceRequestType == sr.type) { return 'PlayReadyDomainJoinServiceRequest'; } else if (Microsoft.Media.PlayReadyClient.PlayReadyStatics.domainLeaveServiceRequestType == sr.type) { return 'PlayReadyDomainLeaveServiceRequest'; } else if (Microsoft.Media.PlayReadyClient.PlayReadyStatics.meteringReportServiceRequestType == sr.type) { return 'PlayReadyMeteringServiceRequest'; } else { return '( UNKNOWN TYPE! )'; } } catch (e) { logError(e, 'getSRType'); } }
This function returns a string that identifies the type of service being requested.
Add the following function at the end of the file:
function HandleServiceRequest(serviceRequest) { try { logMsg('------ SRtype: ' + getSRType(serviceRequest)); g_CurrentServiceRequest = serviceRequest; g_CurrentSRString = getSRType(serviceRequest); prepareSR(serviceRequest); logMsg('--- Querying Data set on service request\n'); logMsg('------ Protection System ID:' + serviceRequest.protectionSystem); logMsg('------ Service request type:' + getSRType(serviceRequest)); if (serviceRequest.uri != null) { logMsg('------ Default Uri:' + serviceRequest.uri.absoluteUri); } else { logMsg('------ Default Uri: (NULL)\n'); } logMsg('--- Starting PlayReadyServiceRequest processing '); var onServiceRequestSuccessWrapper = function () { onServiceRequestSuccess(); } var onServiceRequestErrorWrapper = function () { onServiceRequestError(); } logMsg('--- using promises model ( serviceRequest.then(..) )'); serviceRequest.beginServiceRequest().then(onServiceRequestSuccessWrapper, onServiceRequestErrorWrapper); } catch (e) { logError(e, 'HandleServiceRequest'); } }
This function first calls the prepareSR function to in case any custom data is required to prepare the service request. It then sets up the functions to be used once the begin service request is fulfilled using the then method.
Add the following function to the end of the file:
function prepareSR(serviceRequest) { if (Microsoft.Media.PlayReadyClient.PlayReadyStatics.licenseAcquirerServiceRequestType == serviceRequest.type) { configLASR(serviceRequest); } else if (Microsoft.Media.PlayReadyClient.PlayReadyStatics.domainJoinServiceRequestType == serviceRequest.type) { configDJSR(serviceRequest); } }
This function prepares the service request by calling functions that require custom data to be sent out. In this case, a license service request that requires custom data can be defined in the configLASR function, and custom data required by a domain join can be defined in the configDJSR function. In this sample, however, domain join is not used.
Add the following function to the end of the file:
function configLASR(objLASR) { try { if (objLASR.uri != null) { logMsg('--- current serviceRequest URL = ' + objLASR.uri.absoluteUri); } if (objLASR.challengeCustomData != null) { logMsg('--- current serviceRequest challengeCustomData = ' + objLASR.challengeCustomData); } var lauri; var txtlaURL = LAURL; var txtlaChlgCustData = "Custom Data"; logMsg('--- override lauri = ' + txtlaURL); lauri = Windows.Foundation.Uri(txtlaURL); objLASR.uri = lauri; logMsg('--- setting custom data - objLASR.challengeCustomData = ' + txtlaChlgCustData); objLASR.challengeCustomData = txtlaChlgCustData; logMsg('------ Enabler Key Id: ' + objLASR.contentHeader.keyId); } catch (e) { logWarning(e, 'configLASR'); } }
This function first logs the current service request URL and any current custom data associated with this challenge. It then overrides the current URL with the URL address of the media to be played and overrides the current custom data with any new custom data that is required for the challenge.
Add the following functions to the end of the file:
function onServiceRequestSuccess() { try { logMsg(g_CurrentSRString + ' operation completed successfully'); performSRCompletion(); } catch (e) { logWarning(e, 'onServiceRequestSuccess'); } } function onServiceRequestError() { // We could check for MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED but we'll just ask for a SR var nextServiceRequest = g_CurrentServiceRequest.nextServiceRequest(); if (nextServiceRequest != null) { logMsg('NextServiceRequest returned a new SR'); HandleServiceRequest(nextServiceRequest); } }
The onServiceRequestSuccess function is called if the service request completed successfully. It then calls the performSRCompletion function to complete the service request.
The onServiceRequestError function is called if an error occurs during the service request. In this sample, the function does nothing with the error, but instead checks for a new service request and, if one is available, it calls the HandleServiceRequest function to handle the new service request. You could instead check for the type of error that has occurred and handle the consequences appropriately.
Add the following function to the end of the file:
function performSRCompletion() { if (g_CurrentSRString == 'IndividualizationServiceRequest') { logMsg('playready security version is ' + Microsoft.Media.PlayReadyClient.PlayReadyStatics.playReadySecurityVersion); } // All ServiceRequests are done. Fire completion if necessary if (g_PostSRCompleteFunction != null) { logMsg('Call g_PostSRCompleteFunction'); g_PostSRCompleteFunction(0); } else { logMsg('No stored delegate in g_PostSRCompleteFunction'); } }
This function is used to complete the current service request. If the service request was for individualization, the function logs the security version. It then calls the g_PostSRCompleteFunction function defined in the serviceRequested function with its value set to true.
Add the following functions to the end of the file:
function MakeUnsigned(number) { return number >>> 0; } function intToHexString(number) { return '0x' + MakeUnsigned(number).toString(16); }
These functions are used by the log message functions to convert numbers to strings for display in the text box on the main page.
To compile and test the app
- On the menu bar, choose BUILD, Configuration Manager.
- In the Configuration Manager dialog, choose your development platform from the Active solution platform list, and then choose the Close button.
- Choose the F6 key to compile the project.
- Choose the F5 key to run the app
- In the app, choose the Play button.