Handling DRM Content

Content Access Framework (CAF) provides services that enable agents to publish content in a generic manner that is easy for applications to use. Applications use the Consumer API to render a content and multimedia plug-ins.

Note: Rendering is a process of reading data from a file, transforming it, then playing or displaying it on the device.

Access to content within archive files

An archive file contains content objects and other containers within the file. Each container within the file may contain more content objects or further containers. Common examples of archive files are zip and tar files.

CAF allows applications to open archive files and read content from inside them. The content objects and containers inside the file can be traversed using the ContentAccess::CContent class. The class allows applications to use the content within these container files without needing to understand any specifics of the compression or storage mechanism used by the archive.

Required background

Before you start, you must understand:

Introduction

Content Access Framework also allows applications to open archive files and read content from inside them. The content objects and containers inside the file can be traversed using the ContentAccess::CContent class. This class allows applications to use the content within the container files without needing to understand any specifics of the compression or storage mechanism used by the archive. See Files Containing Multiple Content Objects for more information.

The following diagram is an example of a content file. The same diagram is used in the procedure to illustrate the implementation of Consumer API.

Figure: Example content file

Procedure

  1. Create a content object.

  2. Perform one or more of the following tasks as per your requirement:

Create a CContent object

The ContentAccess::CContent object encapsulates a single file. It allows an application to look at the structure of the objects within the file and the attributes of those objects.

There are two ways to create a CContent object. The application can specify the URI of the content or it can supply an open file handle, see Objects used to identify a content object within a file.

// Create a CContent with a URI 
CContent *content = CContent::NewL(uri); 

// Create a CContent with an existing file handle
CContent *content = CContent::NewL(aFs, aFile);

Upon creation, CContent selects the agent that handles the file.

List objects within a container

ContentAccess::CContent acts like a cursor, only able to list the contents of one container object at any one time. When CContent is first opened, it views the top-level container within the file. The top level container is actually the file itself. This top level container concept applies to all files, regardless of how many content or container objects are inside.

Even a content file such as a jpeg image is a container, it's just that the file only has the "DEFAULT" object embedded inside.

So, when the example file shown earlier is opened, the following objects can be seen by the CContent:

In this top level container, there is only one embedded content object visible (the .jpg file) and two embedded container objects.

// Create an array to store the results of CContent::GetEmbeddedObjectsL() 
RStreamablePtrArray<CEmbeddedObject> myArray; 
CleanupClosePushL(myArray); 
    
// Get the embedded content objects in the current container 
content->GetEmbeddedObjectsL(myArray, EContentObject); 

i = myArray.Count(); // One content object 

// clear the contents of the array 
myArray.ResetAndDestroy(); 
    
// Get the number of container objects in the current container
content->GetEmbeddedObjectsL(myArray, EContainerObject); 

i = myArray.Count(); // Two container objects 
                    
// clear the contents of the array 
myArray->ResetAndDestroy();

Open a container

To investigate the objects inside a container, ContentAccess::Content must first open the container. This changes CContent's focus from the current container to the container specified in the ContentAccess::CContent::OpenContainer() function.

Open container 1 from the top level of the file

// Get the container objects in the top level of the file 
content->GetEmbeddedObjects(myArray, EContainerObject); 

// Find the Unique Id of the first container 
TPtrC UniqueId = myArray[0]->UniqueId() 

// Open the first container 
content->OpenContainer(UniqueId);

Now CContent can see the contents of Container 1:

At this point, listing the objects that CContent can see gives six MP3 files and one container object.

// Get the embedded content objects in the current container 
content->GetEmbeddedObjectsL(myArray, EContentObject); 
                 
i = myArray.Count(); // Six content objects
myArray.ResetAndDestroy(); 
                    
// Get the number of container objects in the current container 
content->GetEmbeddedObjectsL(myArray, EContainerObject);
                          
i = myArray.Count(); // One container object 
myArray.ResetAndDestroy();

Note: The same process can be followed again to see the contents of Container 1.1.

Open the metadata specific to an agent

Some agents may expose metadata so they can be read using a ContentAccess::CData object. The format of these meta-data objects is not specified by the Content Access Framework but can be useful for applications familiar with the agent to read meta data this way.

CData objects for agent specific metadata can be opened in the same way content objects are opened, using the ContentAccess::CContent::OpenContentL() function.

// Create an array to store the embedded objects 
...

// Get the embedded "Agent Specific" objects in the current container
content->GetEmbeddedObjectsL(myArray, EAgentSpecificObject); 

// Get the unique Id of the first meta-data object 
TPtrC uniqueId = myArray[0]->UniqueId();

// create a CData object to read the meta data 
CData *myMetaData = content->OpenContent(EPeek, uniqueId);

Display content object information

The ContentAccess::CContent::DisplayInfoL() function allows the application to display file information, such as last modification date and DRM rights information.

...
content->DisplayInfoL(TDisplayInfo  aInfo);
...

Retrieve the attributes of a content

Content and container objects have properties or attributes associated with it. Applications can retrieve single or mulitple attributes using the ContentAccess::TAttributeCAF enumeration and the ContentAccess::TStringAttribute enumeration.

The following example finds the author attribute of a content object.

// define a buffer to store the attribute value string 
TBuf <100> buf; 
    
// retrieve the attribute 
err = content->GetAttribute(EAuthor, buf, uniqueId); 

// Display the author's name on screen 
DisplayAuthor(buf);

Search for a MIME type within a file

If an application wants to find all the content with a particular MIME type within a file, it must use ContentAccess::CContent::Search(). This function produces a list of all content objects with the specified MIME type that are stored under the current container.

// Create an array for storing the result of the search 
RStreamablePtrArray<CEmbeddedObject> myArray; 

// Find how many MP3 files are in Container 1 
TInt numMp3 = content->Search(myArray, _L("mpeg/audio"), EFalse);

Read data from a content object

The functions described earlier can be used to locate a particular content object within a file. ContentAccess::CContent::OpenContentL() can be used to read the content object. The UniqueId parameter can be used to identify a particular object within the file.

The call to ContentAccess::CContent::OpenContentL() leaves if the intent is not permitted. This could occur if the file is DRM-protected but no rights are present.

If the file is DRM protected and the call to OpenContentL() succeeds, the rights are not consumed at this point. CAF just checks that it is possible to use the content.

// Open the content object specified by uniqueId with the EPlay intent 
CData* data = content->OpenContentL(EPlay, uniqueId);

If the application already knows the URI and unique ID of the content object it wants to read from, it can create a CData object directly.

CData* data = CData::NewL(TVirtualPathPtr(uri, uniqueId), EPlay, EContentShareReadOnly);

Once the CData object has been constructed, it allows the content object to be used as if it were a standalone unprotected file. The client must call ContentAccess::CData::ExecuteIntent() when the rights must be consumed. If the file is not DRM-protected, the call is ignored by the agent handling the file.

TBuf8 <256> buf; 
data->ExecuteIntent(EPlay); 
data->Seek(ESEEK_START,SomePosition); 
data->Read(buf);

There are several overloaded versions of the ContentAccess::CData::Read() function. Only one is illustrated above for example purposes.

Handle data corruption

When consuming content it is possible that the content data and rights data can be corrupted. This may be due to various reasons including file or disk corruption, transmission errors and so on.

If an agent detects corruption in a content object or in a rights object, it ends the current DRM operation. The KErrCACorruptContent, KErrCACorruptRights or KErrCorrupt error code is sent to clients, depending on the type of corruption detected and how the client chooses to handle it.

Note: It is recommended that clients be designed to handle such an error if received, as it can usually be considered a fatal error.

// Create a CData object to read the content
...

TRAP(err, data = content->OpenContentLC(EDisplay));

if (err != KErrNone)
        {
        if (err == KErrCACorruptRights)
                {
                //Agent may have performed some error handling. 
                //Client may need to perform some client specific operations(e.g. cleanup).
                }
        else if (err == KErrCorrupt)
                {
                DisplayErrorMsg('Unable to decode rights because it is corrupt!');
                }
        else if (err == ...)
        ...
        }       
...

Handle errors

Agents display the errors on screen when they occur. Once the error has been acknowledged by the user, the agent returns the error code to the client application. If a client application does not want errors displayed on screen, it can request to disable the agent UI using the ContentAccess::CContent::SetProperty() function.

// Disable UI
content->SetProperty(EAgentPropertyAgentUI , 0);

Some agents may present confirmation dialogs to the user before allowing them to perform certain operations. For example, when a user inadvertently tries to delete a DRM-protected file. The agent can present a dialog asking "are you sure?". The agent can then return the outcome of the delete to the application, that is KErrCancel, if the user presses cancel. The ContentAccess::CContent::SetProperty() function can also be used to disable the confirmation dialogs.

Handle rights for DRM content

There are two functions available that give the application some control over the rights:

Request rights

ContentAccess::CContent::RequestRights() allows the application to ask the agent to undertake whatever steps are necessary to obtain rights for the given content object. Some agents may not support this mechanism, in which case they returns KErrCANotSupported.

The request rights call includes a TRequestStatus parameter, which allows the application to be notified of the outcome of the rights request.

content->RequestRights(status, uniqueId);

Display information

ContentAccess::CContent::DisplayInfoL() allows the application to ask the agent to display the file and/or rights information for the given content object. The call returns when the display is dismissed.

Some agents may not support this mechanism, in which case they return KErrCANotSupported.

content->DisplayInfoL(EFileProperties, uniqueId);

Get content object notifications

The CContent interface supports notification requests for content objects within files. The events for which an application can request notification are given by the enumeration ContentAccess::TEventMask.

The following example requests and cancels notification for rights becoming available:

// Request notification when rights become available for a particular content object
content->NotifyStatusChange(ERightsAvailable, status, uniqueId);

// Cancel notification request
content->CancelNotifyStatusChange(status, uniqueId);

Close a container

To close a container, use the ContentAccess::CContent::CloseContainer() function.

For example, Container 1.1 is closed and Container 1 is still open:

//Close Container 1.1 
content->CloseContainer(); 

// Get the embedded content objects in the current container 
content->GetEmbeddedObjectsL(myArray, EContentObject);
i = myArray.Count(); // Six content objects 
myArray.ResetAndDestroy(); 

// Get the number of container objects in the current container 
content->GetEmbeddedObjectsL(myArray, EContainerObject); 
i = myArray.Count(); // One container object 
myArray.ResetAndDestroy();