/*****************************************************
*
* Copyright 2009 Adobe Systems Incorporated. All Rights Reserved.
*
*****************************************************
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
*
* The Initial Developer of the Original Code is Adobe Systems Incorporated.
* Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
* Incorporated. All Rights Reserved.
*
*****************************************************/
package org.osmf.net.drm
{
import flash.errors.IllegalOperationError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.events.StatusEvent;
import flash.utils.ByteArray;
import org.osmf.events.DRMEvent;
import org.osmf.events.MediaError;
import org.osmf.events.MediaErrorCodes;
import org.osmf.traits.DRMState;
import org.osmf.utils.OSMFStrings;
CONFIG::FLASH_10_1
{
import flash.events.DRMAuthenticationCompleteEvent;
import flash.events.DRMAuthenticationErrorEvent;
import flash.events.DRMErrorEvent;
import flash.events.DRMStatusEvent;
import flash.net.drm.AuthenticationMethod;
import flash.net.drm.DRMContentData;
import flash.net.drm.DRMManager;
import flash.net.drm.DRMVoucher;
import flash.net.drm.LoadVoucherSetting;
import flash.net.drm.VoucherAccessInfo;
import flash.system.SystemUpdater;
import flash.system.SystemUpdaterType;
}
[ExcludeClass]
/**
* Dispatched when either anonymous or credential-based authentication is needed in order
* to playback the media.
*
* @eventType org.osmf.events.DRMEvent.DRM_STATE_CHANGE
*/
[Event(name='drmStateChange', type='org.osmf.events.DRMEvent')]
/**
* @private
*
* The DRMServices class is a utility class to adapt the Flash Player's DRM
* to the OSMF-style DRM API. DRMServices handles triggering updates to
* the DRM subsystem, as well as triggering the appropriate events when
* authentication is needed, complete, or failed.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
internal class DRMServices extends EventDispatcher
{
CONFIG::FLASH_10_1
{
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*
*/
public function DRMServices()
{
drmManager = DRMManager.getDRMManager();
}
/**
* The current state of the DRM for this media. The states are explained
* in the DRMState enumeration in the org.osmf.drm package.
* @see DRMState
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function get drmState():String
{
return _drmState;
}
/**
* The metadata property is specific to the DRM for the Flash Player. Once set, authentication
* and voucher retrieval is started. This method may trigger an update to the DRM subsystem. Metadata
* forms the basis for content data.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function set drmMetadata(value:Object):void
{
lastToken = null;
if (value is DRMContentData)
{
drmContentData = value as DRMContentData;
retrieveVoucher();
}
else
{
try
{
drmContentData = new DRMContentData(value as ByteArray);
retrieveVoucher();
}
catch (argError:ArgumentError) // DRM ContentData is invalid
{
updateDRMState
( DRMState.AUTHENTICATION_ERROR,
new MediaError(argError.errorID, "DRMContentData invalid")
);
}
catch (error:IllegalOperationError)
{
function onComplete(event:Event):void
{
updater.removeEventListener(Event.COMPLETE, onComplete);
drmMetadata = value;
}
update(SystemUpdaterType.DRM);
updater.addEventListener(Event.COMPLETE, onComplete);
}
}
}
public function get drmMetadata():Object
{
return drmContentData;
}
/**
* Authenticates the media. Can be used for both anonymous and credential-based
* authentication.
*
* @param username The username. Should be null for anonymous authentication.
* @param password The password. Should be null for anonymous authentication.
*
* @throws IllegalOperationError If the metadata hasn't been set.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function authenticate(username:String = null, password:String = null):void
{
if (drmContentData == null)
{
throw new IllegalOperationError(OSMFStrings.getString(OSMFStrings.DRM_METADATA_NOT_SET));
}
drmManager.addEventListener(DRMAuthenticationErrorEvent.AUTHENTICATION_ERROR, authError);
drmManager.addEventListener(DRMAuthenticationCompleteEvent.AUTHENTICATION_COMPLETE, authComplete);
if (password == null && username == null)
{
retrieveVoucher();
}
else
{
drmManager.authenticate(drmContentData.serverURL, drmContentData.domain, username, password);
}
}
/**
* Authenticates the media using an object which serves as a token. Can be used
* for both anonymous and credential-based authentication.
*
* @param token The token to use for authentication.
*
* @throws IllegalOperationError If the metadata hasn't been set.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function authenticateWithToken(token:Object):void
{
if (drmContentData == null)
{
throw new IllegalOperationError(OSMFStrings.getString(OSMFStrings.DRM_METADATA_NOT_SET));
}
drmManager.setAuthenticationToken(drmContentData.serverURL, drmContentData.domain, token as ByteArray);
retrieveVoucher();
}
/**
* Returns the start date for the playback window. Returns null if authentication
* hasn't taken place.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function get startDate():Date
{
if (voucher != null)
{
return voucher.playbackTimeWindow ? voucher.playbackTimeWindow.startDate : voucher.voucherStartDate;
}
else
{
return null;
}
}
/**
* Returns the end date for the playback window. Returns null if authentication
* hasn't taken place.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function get endDate():Date
{
if (voucher != null)
{
return voucher.playbackTimeWindow ? voucher.playbackTimeWindow.endDate : voucher.voucherEndDate;
}
else
{
return null;
}
}
/**
* Returns the length of the playback window, in seconds. Returns NaN if
* authentication hasn't taken place.
*
* Note that this property will generally be the difference between startDate
* and endDate, but is included as a property because there may be times where
* the duration is known up front, but the start or end dates are not (e.g. a
* one week rental).
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function get period():Number
{
if (voucher != null)
{
return voucher.playbackTimeWindow ? voucher.playbackTimeWindow.period : (voucher.voucherEndDate && voucher.voucherStartDate) ? (voucher.voucherEndDate.time - voucher.voucherStartDate.time)/1000 : 0;
}
else
{
return NaN;
}
}
/**
* @private
*
* Signals failures from the DRMsubsystem not captured though the
* DRMServices class.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function inlineDRMFailed(error:MediaError):void
{
updateDRMState(DRMState.AUTHENTICATION_ERROR, error);
}
/**
* @private
* Signals DRM is available, taken from the inline netstream APIs.
* Assumes the voucher is available.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function inlineOnVoucher(event:DRMStatusEvent):void
{
drmContentData = event.contentData;
onVoucherLoaded(event);
}
/**
* @private
*
* Triggers and update of the DRM subsystem.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
public function update(type:String):SystemUpdater
{
updateDRMState(DRMState.DRM_SYSTEM_UPDATING);
if (updater == null) //An update hasn't been triggered
{
updater = new SystemUpdater();
//If there is an update already happening, just wait for it to finish.
toggleErrorListeners(updater, true);
updater.update(type);
}
else
{
//If there is an update already happening, just wait for it to finish.
toggleErrorListeners(updater, true);
}
return updater;
}
// Internals
//
/**
* Downloads the voucher for the metadata specified.
*
* @langversion 3.0
* @playerversion Flash 10.1
* @playerversion AIR 1.5
* @productversion OSMF 1.0
*/
private function retrieveVoucher():void
{
updateDRMState(DRMState.AUTHENTICATING);
drmManager.addEventListener(DRMErrorEvent.DRM_ERROR, onDRMError);
drmManager.addEventListener(DRMStatusEvent.DRM_STATUS, onVoucherLoaded);
drmManager.loadVoucher(drmContentData, LoadVoucherSetting.ALLOW_SERVER);
}
private function onVoucherLoaded(event:DRMStatusEvent):void
{
if (event.contentData == drmContentData)
{
var now:Date = new Date();
if (event.voucher &&
(
( event.voucher.voucherEndDate == null
|| event.voucher.voucherEndDate.time >= now.time)
&& ( event.voucher.voucherStartDate == null
|| event.voucher.voucherStartDate.time <= now.time)
)
)
{
this.voucher = event.voucher;
removeEventListeners();
if (voucher.playbackTimeWindow == null)
{
updateDRMState(DRMState.AUTHENTICATION_COMPLETE, null, voucher.voucherStartDate, voucher.voucherEndDate, period, lastToken);
}
else
{
updateDRMState(DRMState.AUTHENTICATION_COMPLETE, null, voucher.playbackTimeWindow.startDate, voucher.playbackTimeWindow.endDate, voucher.playbackTimeWindow.period, lastToken);
}
}
else //Only force refresh if voucher was good, and has expired (local voucher).
{
forceRefreshVoucher();
}
}
}
private function forceRefreshVoucher():void
{
drmManager.loadVoucher(drmContentData, LoadVoucherSetting.FORCE_REFRESH);
}
private function onDRMError(event:DRMErrorEvent):void
{
if (event.contentData == drmContentData) //Ensure this event is for our data.
{
switch(event.errorID)
{
case DRM_CONTENT_NOT_YET_VALID:
forceRefreshVoucher();
break;
case DRM_NEEDS_AUTHENTICATION:
updateDRMState(DRMState.AUTHENTICATION_NEEDED, null, null, null, 0, null, event.contentData.serverURL );
break;
default:
removeEventListeners();
updateDRMState(DRMState.AUTHENTICATION_ERROR, new MediaError(event.errorID, event.text));
break;
}
}
}
private function removeEventListeners():void
{
drmManager.removeEventListener(DRMErrorEvent.DRM_ERROR, onDRMError);
drmManager.removeEventListener(DRMStatusEvent.DRM_STATUS, onVoucherLoaded);
}
private function authComplete(event:DRMAuthenticationCompleteEvent):void
{
drmManager.removeEventListener(DRMAuthenticationErrorEvent.AUTHENTICATION_ERROR, authError);
drmManager.removeEventListener(DRMAuthenticationCompleteEvent.AUTHENTICATION_COMPLETE, authComplete);
lastToken = event.token;
retrieveVoucher();
}
private function authError(event:DRMAuthenticationErrorEvent):void
{
drmManager.removeEventListener(DRMAuthenticationErrorEvent.AUTHENTICATION_ERROR, authError);
drmManager.removeEventListener(DRMAuthenticationCompleteEvent.AUTHENTICATION_COMPLETE, authComplete);
updateDRMState(DRMState.AUTHENTICATION_ERROR, new MediaError(event.errorID, event.toString()));
}
private function toggleErrorListeners(updater:SystemUpdater, on:Boolean):void
{
if (on)
{
updater.addEventListener(Event.COMPLETE, onUpdateComplete);
updater.addEventListener(Event.CANCEL, onUpdateComplete);
updater.addEventListener(IOErrorEvent.IO_ERROR, onUpdateError);
updater.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onUpdateError);
updater.addEventListener(StatusEvent.STATUS, onUpdateError);
}
else
{
updater.removeEventListener(Event.COMPLETE, onUpdateComplete);
updater.removeEventListener(Event.CANCEL, onUpdateComplete);
updater.removeEventListener(IOErrorEvent.IO_ERROR, onUpdateError);
updater.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onUpdateError);
updater.removeEventListener(StatusEvent.STATUS, onUpdateError);
}
}
private function onUpdateComplete(event:Event):void
{
toggleErrorListeners(updater, false);
}
private function onUpdateError(event:Event):void
{
toggleErrorListeners(updater, false);
updateDRMState(DRMState.AUTHENTICATION_ERROR, new MediaError(MediaErrorCodes.DRM_SYSTEM_UPDATE_ERROR, event.toString()));
}
private function updateDRMState(newState:String, error:MediaError = null, start:Date = null, end:Date = null, period:Number = 0 , token:Object=null, prompt:String = null ):void
{
_drmState = newState;
dispatchEvent
( new DRMEvent
( DRMEvent.DRM_STATE_CHANGE,
newState,
false,
false,
start,
end,
period,
prompt,
token,
error
)
);
}
// The flash player's internal DRM error codes
/**
* @private
*/
private static const DRM_AUTHENTICATION_FAILED:int = 3301;
/**
* @private
*/
private static const DRM_NEEDS_AUTHENTICATION:int = 3330;
/**
* @private
*/
private static const DRM_CONTENT_NOT_YET_VALID:int = 3331;
private var _drmState:String = DRMState.UNINITIALIZED;
private var lastToken:ByteArray
private var drmContentData:DRMContentData;
private var voucher:DRMVoucher;
private var drmManager:DRMManager;
//this is static, since the SystemUpdater needs to be trated as a singleton. Only one update at a time.
private static var updater:SystemUpdater;
}
}
}