mmlibs/mmfw/src/ControllerFramework/mmfcontrollerserver.cpp
changeset 0 b8ed18f6c07b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmlibs/mmfw/src/ControllerFramework/mmfcontrollerserver.cpp	Thu Oct 07 22:34:12 2010 +0100
@@ -0,0 +1,790 @@
+// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <mmf/common/mmfcontroller.h>
+#include <mmf/server/mmffile.h>
+#include <mmf/common/mmfdrmcustomcommands.h>
+#include <mmf/common/mmfstandardcustomcommands.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <mmf/common/mmfcontrollerextendeddata.h>
+#include <mmf/common/mmfcustomcommandparsermanager.h>
+#endif
+
+inline void ExtendedDataTlsCleanup(TAny* /*aPtr*/)
+	{
+	delete static_cast<CMMFControllerExtendedData*>(Dll::Tls());
+	Dll::SetTls(NULL);
+	}
+
+CMMFController* CMMFController::NewL(TUid aControllerUid, MAsyncEventHandler& aEventHandler, TThreadId aClientTid)
+	{
+	Dll::SetTls(CMMFController::CreateExtendedDataL(aClientTid));
+	CleanupStack::PushL(TCleanupItem(&ExtendedDataTlsCleanup, NULL));
+	CMMFController* s = REINTERPRET_CAST(CMMFController*,REComSession::CreateImplementationL(aControllerUid,_FOFF(CMMFController,iDtor_ID_Key)));
+	CleanupStack::PushL(s);
+	s->ConstructL(aEventHandler, aClientTid);
+	CleanupStack::Pop(s);
+	CleanupStack::PopAndDestroy();	// TCleanupItem(ExtendedDataTlsCleanup)
+	return s;
+	}
+
+void CMMFController::ConstructL(MAsyncEventHandler& aEventHandler, TThreadId aClientTid)
+	{
+	iAsyncEventHandler = &aEventHandler;
+	
+	// If extended data hasn't been initialized, GetExtendedDataL will initialize it and
+	// add it into iMMFObjectContainer
+#ifdef _DEBUG
+	CMMFControllerExtendedData* extendData =
+#endif
+		GetExtendedDataL();
+#ifdef _DEBUG
+	ASSERT(extendData->ClientThreadId() == aClientTid);
+#else
+	(void)aClientTid;	// remove compilation error
+#endif
+	}
+
+EXPORT_C CMMFController::~CMMFController()
+	{
+	delete iCustomCommandParserManager;
+	delete iMMFObjectContainer;
+	delete iMetaDataBuffer;
+	REComSession::DestroyedImplementation(iDtor_ID_Key);
+	}
+
+EXPORT_C TInt CMMFController::DoSendEventToClient(const TMMFEvent& aEvent)
+	{
+	return iAsyncEventHandler->SendEventToClient(aEvent);
+	}
+
+EXPORT_C CMMFObjectContainer& CMMFController::MMFObjectContainerL()
+	{
+	// Construct iMMFObjectContainer if we haven't already.
+	if (!iMMFObjectContainer)
+		iMMFObjectContainer = new(ELeave) CMMFObjectContainer;
+	return *iMMFObjectContainer;
+	}
+
+EXPORT_C void CMMFController::AddCustomCommandParserL(CMMFCustomCommandParserBase& aParser)
+	{
+	// Construct iCustomCommandParserManager if we haven't already
+	if (!iCustomCommandParserManager)
+		iCustomCommandParserManager = CMMFCustomCommandParserManager::NewL();
+	iCustomCommandParserManager->AddCustomCommandParserL(aParser);
+	}
+
+
+EXPORT_C void CMMFController::HandleRequestL(TMMFMessage& aMessage)
+	{
+	// If the message has the constant controller destination handle, pass the message to
+	// the controller plugin to handle.
+	// Otherwise, the message must be for one of the MMFObjects.
+	if (aMessage.Destination().DestinationHandle() == KMMFObjectHandleController)
+		{
+		// If the message has an interface ID for this controller, handle it here.
+		// Otherwise, pass it to the controller plugin to handle as a custom command.
+		if (aMessage.Destination().InterfaceId() == KUidInterfaceMMFController)
+			{
+			TBool complete = ETrue;
+			switch (aMessage.Function())
+				{
+			case EMMFControllerAddDataSource:
+				complete = DoAddDataSourceL(aMessage);
+				break;
+			case EMMFControllerAddDataSink:
+				complete = DoAddDataSinkL(aMessage);
+				break;
+			case EMMFControllerRemoveDataSource:
+				complete = DoRemoveDataSourceL(aMessage);
+				break;
+			case EMMFControllerRemoveDataSink:
+				complete = DoRemoveDataSinkL(aMessage);
+				break;
+			case EMMFControllerReset:
+				complete = DoResetL(aMessage);
+				break;
+			case EMMFControllerPrime:
+				complete = DoPrimeL(aMessage);
+				break;
+			case EMMFControllerPlay:
+				complete = DoPlayL(aMessage);
+				break;
+			case EMMFControllerPause:
+				complete = DoPauseL(aMessage);
+				break;
+			case EMMFControllerStop:
+				complete = DoStopL(aMessage);
+				break;
+			case EMMFControllerGetPosition:
+				complete = DoGetPositionL(aMessage);
+				break;
+			case EMMFControllerSetPosition:
+				complete = DoSetPositionL(aMessage);
+				break;
+			case EMMFControllerGetDuration:
+				complete = DoGetDurationL(aMessage);
+				break;
+			case EMMFControllerGetNumberOfMetaDataEntries:
+				complete = DoGetNumberOfMetaDataEntriesL(aMessage);
+				break;
+			case EMMFControllerGetSizeOfMetaDataEntry:
+				complete = DoGetSizeOfMetaDataEntryL(aMessage);
+				break;
+			case EMMFControllerGetMetaDataEntry:
+				complete = DoGetMetaDataEntryL(aMessage);
+				break;
+			case EMMFControllerSetPrioritySettings:
+				complete = DoSetPrioritySettingsL(aMessage);
+				break;
+			case EMMFControllerCancelAddDataSource:
+				complete = ETrue;//Nothing to cancel since AddDataSource is synchronous server-side
+				break;
+			case EMMFControllerCancelAddDataSink:
+				complete = ETrue;//Nothing to cancel since AddDataSink is synchronous server-side
+				break;
+			case EMMFControllerAddFileHandleDataSource:
+				complete = DoAddFileHandleDataSourceL(aMessage);
+				break;
+			case EMMFControllerAddFileHandleDataSink:
+				complete = DoAddFileHandleDataSinkL(aMessage);
+				break;
+			case EMMFControllerSourceSinkInitDataPreload:
+				complete = DoPreloadSourceSinkInitDataL(aMessage);
+				break;
+			case EMMFControllerAddFileHandleDataSourceWithInitData:
+				complete = DoAddFileHandleDataSourceWithInitDataL(aMessage);
+				break;
+			case EMMFControllerAddFileHandleDataSinkWithInitData:
+				complete = DoAddFileHandleDataSinkWithInitDataL(aMessage);
+				break;
+			default:
+				User::Leave(KErrNotSupported);
+				break;
+				}
+			if (complete)
+				aMessage.Complete(KErrNone);
+			}
+		else
+			{
+			// Must be a custom command
+			DoCustomCommand(aMessage);
+			}
+		}
+	else
+		{
+		// Message for one of the MMF Objects
+		CMMFObject* object = NULL;
+		User::LeaveIfError(MMFObjectContainerL().FindMMFObject(aMessage.Destination(), object));
+		object->HandleRequest(aMessage);
+		}
+
+	}
+
+TBool CMMFController::DoAddDataSourceL(TMMFMessage& aMessage)
+	{
+	// Get the UID of the source from the client
+	TMMFUidPckg uidPckg;
+	aMessage.ReadData1FromClientL(uidPckg);
+
+	// Get the size of the init data and create a buffer to hold it
+	TInt desLength = aMessage.SizeOfData2FromClient();
+	// Leaving here in order to prevent a panic in the NewLC if the value is negative
+	User::LeaveIfError(desLength); 
+	HBufC8* buf = HBufC8::NewLC(desLength);
+	TPtr8 ptr = buf->Des();
+	aMessage.ReadData2FromClientL(ptr);
+
+	// Create the source
+	MDataSource* source = MDataSource::NewSourceL(uidPckg(), *buf);
+	CleanupStack::PopAndDestroy(buf);//buf
+	AddMDataSourceAndRepondClientL(source, aMessage);
+	
+	return ETrue;
+	}
+	
+	
+TBool CMMFController::DoAddDataSinkL(TMMFMessage& aMessage)
+	{
+	// Get the UID of the sink from the client
+	TMMFUidPckg uidPckg;
+	aMessage.ReadData1FromClientL(uidPckg);
+
+	// Get the size of the init data and create a buffer to hold it
+	TInt desLength = aMessage.SizeOfData2FromClient();
+	// Leaving here in order to prevent a panic in the NewLC if the value is negative
+	User::LeaveIfError(desLength);
+	HBufC8* buf = HBufC8::NewLC(desLength);
+	TPtr8 ptr = buf->Des();
+	aMessage.ReadData2FromClientL(ptr);
+
+	// Create the sink
+	MDataSink* sink = MDataSink::NewSinkL(uidPckg(), *buf);
+	CleanupStack::PopAndDestroy(buf);//buf
+	AddMDataSinkAndRepondClientL(sink, aMessage);
+	
+	return ETrue;
+	}
+	
+TBool CMMFController::DoRemoveDataSourceL(TMMFMessage& aMessage)
+	{
+	TMMFMessageDestinationPckg handleInfo;
+	aMessage.ReadData1FromClientL(handleInfo);
+	
+	// Find the correct source
+	CMMFObject* object = NULL;
+	CMMFDataSourceHolder* holder = NULL;
+	User::LeaveIfError(MMFObjectContainerL().FindMMFObject(handleInfo(), object));
+	// Cast the object found if possible..
+	if (object->Handle().InterfaceId() == KUidInterfaceMMFDataSourceHolder)
+		holder = STATIC_CAST(CMMFDataSourceHolder*, object);
+	else
+		User::Leave(KErrBadHandle);
+
+	// Try to remove the data source from the plugin
+	RemoveDataSourceL(holder->DataSource());
+
+	// If we're here, the removal worked so remove the source from the object array and delete it
+	MMFObjectContainerL().RemoveAndDestroyMMFObject(*holder);
+	return ETrue;
+	}
+
+TBool CMMFController::DoRemoveDataSinkL(TMMFMessage& aMessage)
+	{
+	TMMFMessageDestinationPckg handleInfo;
+	aMessage.ReadData1FromClientL(handleInfo);
+	
+	// Find the correct source
+	CMMFObject* object = NULL;
+	CMMFDataSinkHolder* holder = NULL;
+	User::LeaveIfError(MMFObjectContainerL().FindMMFObject(handleInfo(), object));
+	// Cast the object found if possible..
+	if (object->Handle().InterfaceId() == KUidInterfaceMMFDataSinkHolder)
+		holder = STATIC_CAST(CMMFDataSinkHolder*, object);
+	else
+		User::Leave(KErrBadHandle);
+
+	// Try to remove the data sink from the plugin
+	RemoveDataSinkL(holder->DataSink());
+
+	// If we're here, the removal worked so remove the source from the object array and delete it
+	MMFObjectContainerL().RemoveAndDestroyMMFObject(*holder);
+	return ETrue;
+	}
+
+TBool CMMFController::DoResetL(TMMFMessage& /*aMessage*/)
+	{
+	ResetL();
+	// Controller extended data must be kept until the death of controller
+	const RPointerArray<CMMFObject>& objects = iMMFObjectContainer->MMFObjects();
+	for (TInt i = objects.Count()-1; i >= 0; i--)
+		{
+		CMMFObject* object = objects[i];
+		if (object->Handle().InterfaceId() != KUidMMFControllerExtendedDataHolder)
+			{
+			iMMFObjectContainer->RemoveAndDestroyMMFObject(*object);
+			}
+		}
+	return ETrue;
+	}
+
+TBool CMMFController::DoPrimeL(TMMFMessage& aMessage)
+	{
+	PrimeL(aMessage);
+	return EFalse;
+	}
+
+
+EXPORT_C void CMMFController::PrimeL(TMMFMessage& aMessage)
+	{
+	PrimeL();
+	aMessage.Complete(KErrNone);
+	}
+
+
+TBool CMMFController::DoPlayL(TMMFMessage& aMessage)
+	{
+	PlayL(aMessage);
+	return EFalse;
+	}
+
+EXPORT_C void CMMFController::PlayL(TMMFMessage& aMessage)
+	{
+	PlayL();
+	aMessage.Complete(KErrNone);
+	}
+
+
+
+TBool CMMFController::DoPauseL(TMMFMessage& aMessage)
+	{
+	PauseL(aMessage);
+	return EFalse;
+	}
+
+EXPORT_C void CMMFController::PauseL(TMMFMessage& aMessage)
+	{
+	PauseL();
+	aMessage.Complete(KErrNone);
+	}
+
+
+TBool CMMFController::DoStopL(TMMFMessage& aMessage)
+	{
+	StopL(aMessage);
+	return EFalse;
+	}
+
+EXPORT_C void CMMFController::StopL(TMMFMessage& aMessage)
+	{
+	StopL();
+	aMessage.Complete(KErrNone);
+	}
+
+
+
+TBool CMMFController::DoGetPositionL(TMMFMessage& aMessage)
+	{
+	TMMFTimeIntervalMicroSecondsPckg pckg(PositionL());
+	aMessage.WriteDataToClientL(pckg);
+	return ETrue;
+	}
+
+TBool CMMFController::DoSetPositionL(TMMFMessage& aMessage)
+	{
+	TMMFTimeIntervalMicroSecondsPckg pckg;
+	aMessage.ReadData1FromClientL(pckg);
+	SetPositionL(pckg());
+	return ETrue;
+	}
+
+TBool CMMFController::DoGetDurationL(TMMFMessage& aMessage)
+	{
+	TMMFTimeIntervalMicroSecondsPckg pckg(DurationL());
+	aMessage.WriteDataToClientL(pckg);
+	return ETrue;
+	}
+
+TBool CMMFController::DoSetPrioritySettingsL(TMMFMessage& aMessage)
+	{
+	TMMFPrioritySettingsPckg pckg;
+	aMessage.ReadData1FromClientL(pckg);
+	SetPrioritySettings(pckg());
+	return ETrue;
+	}
+
+void CMMFController::DoCustomCommand(TMMFMessage& aMessage)
+	{
+	// First, try giving the message to the custom command parser manager...
+	TBool handled = EFalse;
+	if (iCustomCommandParserManager)
+		{
+		if (aMessage.Destination().InterfaceId() == KUidInterfaceMMFDRMControl ||
+			aMessage.Destination().InterfaceId() == KUidInterfaceMMFVideoDRMExt)
+			{
+			TBool secureDrmMode = EFalse;
+			TRAPD(err, secureDrmMode = GetExtendedDataL()->SecureDrmMode());
+			if (err)
+				{
+				aMessage.Complete(err);
+				handled = ETrue;
+				}
+			else if (secureDrmMode)
+				{
+				aMessage.Complete(KErrPermissionDenied);
+				handled = ETrue;
+				}
+			}
+		
+		if (!handled)
+			{
+			handled = iCustomCommandParserManager->HandleRequest(aMessage);
+			}
+		}
+		
+	// If the ccp manager couldn't handle the message, pass directly to the 
+	// controller plugin as a custom command.
+	if (!handled)
+		CustomCommand(aMessage);
+	}
+
+TBool CMMFController::DoGetNumberOfMetaDataEntriesL(TMMFMessage& aMessage)
+	{
+	TInt numberOfEntries;
+	GetNumberOfMetaDataEntriesL(numberOfEntries);
+	TPckgBuf<TInt> pckg(numberOfEntries);
+	aMessage.WriteDataToClientL(pckg);
+	return ETrue;
+	}
+
+TBool CMMFController::DoGetSizeOfMetaDataEntryL(TMMFMessage& aMessage)
+	{
+	// Get the index of the required entry
+	TPckgBuf<TInt> pckg;
+	aMessage.ReadData1FromClientL(pckg);
+	// Get the entry
+	CMMFMetaDataEntry* entry = GetMetaDataEntryL(pckg());
+	CleanupStack::PushL(entry);
+	// Delete any existing buffer
+	delete iMetaDataBuffer;
+	iMetaDataBuffer = NULL;
+	// Create a buffer to hold the externalised entry
+	iMetaDataBuffer = CBufFlat::NewL(32);
+	RBufWriteStream s;
+	s.Open(*iMetaDataBuffer);
+	CleanupClosePushL(s);
+	entry->ExternalizeL(s);
+	CleanupStack::PopAndDestroy(2);//s, entry
+	// Write the size of the externalised data back to the client
+	pckg() = iMetaDataBuffer->Ptr(0).Length();
+	aMessage.WriteDataToClientL(pckg);
+	return ETrue;
+	}
+
+TBool CMMFController::DoGetMetaDataEntryL(TMMFMessage& aMessage)
+	{
+	// We should have already prepared the buffer
+	if (!iMetaDataBuffer)
+		User::Leave(KErrNotReady);
+	aMessage.WriteDataToClientL(iMetaDataBuffer->Ptr(0));
+	return ETrue;
+	}
+
+TBool CMMFController::DoPreloadSourceSinkInitDataL(TMMFMessage& aMessage)
+	{
+	GetExtendedDataL()->ResetSourceSinkInitData();
+	
+	// Get the size of the init data and create a buffer to hold it
+	TInt desLength = aMessage.SizeOfData1FromClient();
+	HBufC8* sourceSinkInitData = HBufC8::NewLC(desLength);
+	TPtr8 ptr = sourceSinkInitData->Des();
+	aMessage.ReadData1FromClientL(ptr);
+	
+	GetExtendedDataL()->SetSourceSinkInitData(sourceSinkInitData);
+	CleanupStack::Pop(sourceSinkInitData);
+	return ETrue;
+	}
+
+TBool CMMFController::DoAddFileHandleDataSourceWithInitDataL(TMMFMessage& aMessage)
+	{
+	if (GetExtendedDataL()->SourceSinkInitData() == NULL)
+		{
+		User::Leave(KErrNotReady);
+		}
+	HBufC8* initData = GetExtendedDataL()->SourceSinkInitData()->AllocLC();
+	
+	// Get the client file handle and replace 
+	RFile file;
+	aMessage.AdoptFileHandleFromClientL(1,2, file);
+	CleanupClosePushL(file);
+	TPtr8 initDataPtr(initData->Des());
+	ReplaceFileHandleInInitDataL(&file, initDataPtr);
+	
+	// Create the source
+	MDataSource* source = MDataSource::NewSourceL(KUidMmfFileSource, *initData);
+	CleanupStack::PopAndDestroy(2, initData);	//file, initData
+	AddMDataSourceAndRepondClientL(source, aMessage);
+	
+	GetExtendedDataL()->ResetSourceSinkInitData();
+	return ETrue;
+	}
+
+TBool CMMFController::DoAddFileHandleDataSinkWithInitDataL(TMMFMessage& aMessage)
+	{
+	if (GetExtendedDataL()->SourceSinkInitData() == NULL)
+		{
+		User::Leave(KErrNotReady);
+		}
+	HBufC8* initData = GetExtendedDataL()->SourceSinkInitData()->AllocLC();
+	
+	// Get the client file handle and replace
+	RFile file;
+	aMessage.AdoptFileHandleFromClientL(1,2, file);
+	CleanupClosePushL(file);
+	TPtr8 initDataPtr(initData->Des());
+	ReplaceFileHandleInInitDataL(&file, initDataPtr);
+	
+	// Create the sink
+	MDataSink* sink = MDataSink::NewSinkL(KUidMmfFileSink, *initData);
+	CleanupStack::PopAndDestroy(2, initData);	//file, initData
+	AddMDataSinkAndRepondClientL(sink, aMessage);
+	
+	GetExtendedDataL()->ResetSourceSinkInitData();
+	return ETrue;
+	}
+	
+void CMMFController::ReplaceFileHandleInInitDataL(RFile* aFile, TDes8& aInitData)
+	{
+	// aInitData should have the second 4bytes data (first 4bytes represent TUid) containing 
+	// a client RFile pointer.
+	// This RFile pointer may be invalid due to the fact that client and server threads
+	// may reside in different processes, and therefore, different memory space.
+	// In this context, we would rather trust the file handle retrieve from the call
+	// AdoptFileHandleFromClientL, and replace the RFile pointer with a more reliable one
+	TPtr8 filePtrStart = aInitData.MidTPtr(sizeof(TUid));
+	RDesWriteStream writeStream;
+	writeStream.Open(filePtrStart);
+	CleanupClosePushL(writeStream);
+	TPckgBuf<RFile*> filePtr(aFile);
+	writeStream.WriteL(filePtr);
+	writeStream.CommitL();
+	CleanupStack::PopAndDestroy(&writeStream);
+	}
+
+void CMMFController::AddMDataSourceAndRepondClientL(MDataSource* aSource, TMMFMessage& aMessage)
+	{
+	CleanupDeletePushL(aSource);	
+	CMMFDataSourceHolder* holder = new(ELeave) CMMFDataSourceHolder(*aSource);
+	CleanupStack::Pop(aSource);	//aSource (since now owned by holder)
+	
+	CleanupStack::PushL(holder);
+	// Append holder to array of MMFObjects
+	User::LeaveIfError(MMFObjectContainerL().AddMMFObject(*holder));
+	CleanupStack::Pop(holder);//holder
+
+	// Write source handle info back to client
+	TMMFMessageDestination handleInfo(holder->Handle());
+	TMMFMessageDestinationPckg handlePckg(handleInfo);
+	TInt error = aMessage.WriteDataToClient(handlePckg);
+
+	// Add source to plugin
+	if (!error)
+		TRAP(error, AddDataSourceL(*aSource));
+
+	if (error)
+		{
+		// Source not accepted by plugin or we couldn't write handle info back to client, 
+		// so delete it and return error to client
+		MMFObjectContainerL().RemoveAndDestroyMMFObject(*holder);
+		User::Leave(error);
+		}	
+	}
+
+void CMMFController::AddMDataSinkAndRepondClientL(MDataSink* aSink, TMMFMessage& aMessage)
+	{
+	CleanupDeletePushL(aSink);
+	CMMFDataSinkHolder* holder = new(ELeave) CMMFDataSinkHolder(*aSink);
+	CleanupStack::Pop(aSink);	//aSink (since now owned by holder)
+	
+	CleanupStack::PushL(holder);
+	// Append sink to array
+	User::LeaveIfError(MMFObjectContainerL().AddMMFObject(*holder));
+	CleanupStack::Pop(holder);//holder
+
+	// Write sink handle info back to client
+	TMMFMessageDestination handleInfo(holder->Handle());
+	TMMFMessageDestinationPckg handlePckg(handleInfo);
+	TInt error = aMessage.WriteDataToClient(handlePckg);
+
+	// Add sink to plugin
+	if (!error)
+		TRAP(error, AddDataSinkL(*aSink));
+
+	if (error)
+		{
+		// Sink not accepted by plugin or we couldn't write handle info back to client, 
+		// so delete it and return error to client
+		MMFObjectContainerL().RemoveAndDestroyMMFObject(*holder);
+		User::Leave(error);
+		}
+	}
+
+CMMFControllerExtendedData* CMMFController::GetExtendedDataL()
+	{
+	CMMFControllerExtendedData* data = NULL;
+	
+	// iMMFObjectContainer and CMMFControllerExtendedData must be constructed 
+	// in CMMFController constructor. Only 1 CMMFControllerExtendedData object can
+	// be found in iMMFObjectContainer.		
+	const RPointerArray<CMMFObject>& objects = MMFObjectContainerL().MMFObjects();
+	for (TInt i = 0; i < objects.Count(); i++)
+		{
+		if (objects[i]->Handle().InterfaceId() == KUidMMFControllerExtendedDataHolder)
+			{
+			data = static_cast<CMMFControllerExtendedData*>(objects[i]);
+			break;
+			}
+		}
+	if (data == NULL)
+		{
+		// In this case, extended data has not been constructed and added into iMMFObjectContainer.
+		// This is a situation where GetExtendedDataL is being indirectly called in the controller
+		// initialization process (i.e. CMMFController::NewL() or constructor of licensee's controller).
+		// Extended data should have a copy stored in Dll::Tls already at this point.
+		const CMMFControllerExtendedData* dataCopy = static_cast<CMMFControllerExtendedData*>(Dll::Tls());
+		data = CreateExtendedDataL(dataCopy->ClientThreadId());
+		CleanupStack::PushL(data);
+		User::LeaveIfError(MMFObjectContainerL().AddMMFObject(*data));
+		CleanupStack::Pop(data);
+		}
+	return data;
+	}
+
+CMMFControllerExtendedData* CMMFController::CreateExtendedDataL(TThreadId aClientTid)
+	{
+	// In secureDRMMode, SecureDRM server and user are running in
+	// different processes.
+	TProcessId clientProcessId;
+	RThread clientThread;
+	CleanupClosePushL(clientThread);
+	User::LeaveIfError(clientThread.Open(aClientTid));
+	
+	RProcess clientProcess;
+	CleanupClosePushL(clientProcess);
+	User::LeaveIfError(clientThread.Process(clientProcess));
+	clientProcessId = clientProcess.Id();
+	CleanupStack::PopAndDestroy(2, &clientThread);	// clientProcess, clientThread
+	
+	RProcess thisProcess;
+	TBool secureDrmMode = clientProcessId != thisProcess.Id();
+	thisProcess.Close();
+	
+	CMMFControllerExtendedData* dataSet = new(ELeave) CMMFControllerExtendedData();
+	CleanupStack::PushL(dataSet);
+	dataSet->SetClientThreadId(aClientTid);
+	dataSet->SetSecureDrmMode(secureDrmMode);
+	CleanupStack::Pop(dataSet);
+	
+	return dataSet;
+	}
+
+
+EXPORT_C CMMFCustomCommandParserManager* CMMFCustomCommandParserManager::NewL()
+	{
+	return new(ELeave) CMMFCustomCommandParserManager;
+	}
+
+CMMFCustomCommandParserManager::CMMFCustomCommandParserManager()
+	{
+	}
+
+EXPORT_C CMMFCustomCommandParserManager::~CMMFCustomCommandParserManager()
+	{
+	iParsers.ResetAndDestroy();
+	iParsers.Close();
+	}
+
+EXPORT_C TBool CMMFCustomCommandParserManager::HandleRequest(TMMFMessage& aMessage)
+	{
+	TBool handledRequest = EFalse;
+	for (TInt i=0; i<iParsers.Count(); i++)
+		{
+		CMMFCustomCommandParserBase* c = iParsers[i];
+		if (c->InterfaceId() == aMessage.Destination().InterfaceId())
+			{
+			c->HandleRequest(aMessage);
+			handledRequest = ETrue;
+			break;
+			}
+		}
+	return handledRequest;
+	}
+
+EXPORT_C void CMMFCustomCommandParserManager::AddCustomCommandParserL(CMMFCustomCommandParserBase& aParser)
+	{
+	User::LeaveIfError(iParsers.Append(&aParser));
+	}
+
+
+TBool CMMFController::DoAddFileHandleDataSourceL(TMMFMessage& aMessage)
+	{
+	RFile file;
+	aMessage.AdoptFileHandleFromClientL(1,2, file);
+	CleanupClosePushL(file);
+	TMMFFileHandleConfig fileConfig;
+	fileConfig().iFile = &file;
+	// Create the source
+	MDataSource* source = MDataSource::NewSourceL(KUidMmfFileSource, fileConfig);
+	CleanupStack::PopAndDestroy(&file);
+	CleanupDeletePushL(source);	
+	CMMFDataSourceHolder* holder = new(ELeave) CMMFDataSourceHolder(*source);
+	CleanupStack::Pop(source);//source (since now owned by holder)
+
+	CleanupStack::PushL(holder);
+	// Append holder to array of MMFObjects
+	User::LeaveIfError(MMFObjectContainerL().AddMMFObject(*holder));
+	CleanupStack::Pop(holder);//holder
+
+	// Write source handle info back to client
+	TMMFMessageDestination handleInfo(holder->Handle());
+	TMMFMessageDestinationPckg handlePckg(handleInfo);
+	TInt error = aMessage.WriteDataToClient(handlePckg);
+
+	// Add source to plugin
+	if (!error)
+		TRAP(error, AddDataSourceL(*source));
+
+	if (error)
+		{
+		// Source not accepted by plugin or we couldn't write handle info back to client, 
+		// so delete it and return error to client
+		MMFObjectContainerL().RemoveAndDestroyMMFObject(*holder);
+		User::Leave(error);
+		}
+	
+	return ETrue;
+	}
+
+
+TBool CMMFController::DoAddFileHandleDataSinkL(TMMFMessage& aMessage)
+	{
+	RFile file;
+	aMessage.AdoptFileHandleFromClientL(1,2, file);
+	CleanupClosePushL(file);
+	TMMFFileHandleConfig fileConfig;
+	fileConfig().iFile = &file;
+	// Create the sink
+	MDataSink* sink = MDataSink::NewSinkL(KUidMmfFileSink, fileConfig);
+	CleanupStack::PopAndDestroy(&file); // close our handle to the file
+	CleanupDeletePushL(sink);
+	CMMFDataSinkHolder* holder = new(ELeave) CMMFDataSinkHolder(*sink);
+	CleanupStack::Pop(sink);//sink (since now owned by holder)
+
+	CleanupStack::PushL(holder);
+	// Append sink to array
+	User::LeaveIfError(MMFObjectContainerL().AddMMFObject(*holder));
+	CleanupStack::Pop(holder);//holder
+
+
+	// Write sink handle info back to client
+	TMMFMessageDestination handleInfo(holder->Handle());
+	TMMFMessageDestinationPckg handlePckg(handleInfo);
+	TInt error = aMessage.WriteDataToClient(handlePckg);
+
+	// Add sink to plugin
+	if (!error)
+		TRAP(error, AddDataSinkL(*sink));
+
+	if (error)
+		{
+		// Sink not accepted by plugin or we couldn't write handle info back to client, 
+		// so delete it and return error to client
+		MMFObjectContainerL().RemoveAndDestroyMMFObject(*holder);
+		User::Leave(error);
+		}
+	
+	return ETrue;
+	}
+
+EXPORT_C TThreadId CMMFController::ClientThreadIdL()
+	{
+	return GetExtendedDataL()->ClientThreadId();
+	}
+
+EXPORT_C TBool CMMFController::IsSecureDrmModeL()
+	{
+	return GetExtendedDataL()->SecureDrmMode();
+	}
+