// Copyright (c) 2006-2010 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 "surfaceupdateserver.h"
#include <graphics/compositionsurfaceupdate.h>
#include <graphics/extensioncontainer.h>
#include "surfaceupdate.h"
#include "suerror.h"
#ifdef TEST_SURFACE_UPDATE
#include "surfaceupdatetest.h"
#endif
const TUint KDefaultHeapSize=0x10000;
/**
The server maintains session with the clients.
It starts during the initialization of the Content update receiver thread.
*/
CSurfaceUpdateServer* CSurfaceUpdateServer::NewL()
{
CSurfaceUpdateServer* server = new (ELeave) CSurfaceUpdateServer(EPriorityStandard);
CleanupStack::PushL(server);
server->ConstructL();
CleanupStack::Pop();
return server;
}
CSurfaceUpdateServer::CSurfaceUpdateServer(CActive::TPriority aPriority) :
CServer2(aPriority)
{
}
CSurfaceUpdateServer::~CSurfaceUpdateServer()
{
iUpdateReceiver.Close();
iUpdateReceiverPriorityOrder.ResetAndDestroy();
delete iServerProvider;
}
void CSurfaceUpdateServer::ConstructL()
{
iServerProvider = CSurfaceUpdateServerProvider::NewL(EPriorityStandard, this);
}
/**
Assign Content update receiver instance to particular screen.
The following calls of this function will override the previous.
@see MSurfaceUpdateServerProvider::Register
*/
TInt CSurfaceUpdateServer::Register(TInt aScreen, CBase* aUpdateReceiver, TInt aPriority)
{
if(aScreen < 0)
{
return KErrArgument;
}
TInt err = KErrNone;
while((iUpdateReceiver.Count() <= aScreen) && (err == KErrNone))
{
err = iUpdateReceiver.Append(NULL);
}
if(err == KErrNone)
{
TUpdateReceiverPriorityEntry *receiverPriorityEntry = NULL;
if(!aUpdateReceiver)
{//Client wants to unregister the Content update receiver which has been associated with the given screen number.
MCompositionSurfaceUpdate* receiver = iUpdateReceiver[aScreen];
if(receiver)
{
TInt num = iUpdateReceiverPriorityOrder.Count() - 1;
for(;;)
{//Content update receiver must be in priority list, therefore we don't need to check num >= 0
receiverPriorityEntry = iUpdateReceiverPriorityOrder[num];
if(receiverPriorityEntry->iUpdateReceiver == receiver)
{//remove the Content update receiver from the priority list
iUpdateReceiverPriorityOrder.Remove(num);
delete receiverPriorityEntry;
break;
}
num--;
}
iUpdateReceiver[aScreen] = NULL; //unregister the Content update receiver by setting the entry to NULL
}
}
else
{
CExtensionContainer* extCont=static_cast<CExtensionContainer*>(aUpdateReceiver);
MCompositionSurfaceUpdate* updateReceiver=extCont->GetInterface<MCompositionSurfaceUpdate>();
if (updateReceiver)
{
receiverPriorityEntry = new TUpdateReceiverPriorityEntry;
if(!receiverPriorityEntry)
return KErrNoMemory;
receiverPriorityEntry->iPriority = aPriority;
receiverPriorityEntry->iUpdateReceiver = updateReceiver;
err = iUpdateReceiverPriorityOrder.InsertInOrder(receiverPriorityEntry, CSurfaceUpdateServer::CompareUpdateReceiverPriority);
if(err == KErrNone)
{
iUpdateReceiver[aScreen] = updateReceiver;
}
else
{
delete receiverPriorityEntry;
}
}
else
{
err=KErrArgument;
}
}
}
return err;
}
/**
Create a new session with a server. Derived from CSession2.
@param aVersion required version of the server
@param aMessage message from the client.
@return New instance of the CSurfaceUpdateSession.
*/
CSession2* CSurfaceUpdateServer::NewSessionL(const TVersion& aVersion, const RMessage2& /*aMessage*/) const
{
// Check that the version is OK
TVersion v(KSurfaceUpdateServMajorVersionNumber,KSurfaceUpdateServMinorVersionNumber,KSurfaceUpdateServBuildVersionNumber);
if (!User::QueryVersionSupported(v,aVersion))
User::Leave(KErrNotSupported);
// Security is not an issue, any client can have connection to the server
// Create the session.
return new (ELeave) CSurfaceUpdateSession(UpdateReceiverPriority());
}
/**
Utility function to panic the server
@param aPanic Panic code
*/
void CSurfaceUpdateServer::PanicServer(TSurfaceUpdateServPanic aPanic)
{
_LIT(KTxtServerPanic,"Surface Update Server panic");
User::Panic(KTxtServerPanic, aPanic);
}
/**
Provide composition receiver.
@param aScreen targeted screen
@return Composition receiver object, associated to particular screen
*/
MCompositionSurfaceUpdate* CSurfaceUpdateServer::UpdateReceiver(TInt aScreen) const
{
if(aScreen >= iUpdateReceiver.Count())//negative value won't reach this point
return NULL;
return iUpdateReceiver[aScreen];
}
TInt CSurfaceUpdateServer::ThreadFunction(TAny* aAny)
{
// get clean-up stack
CTrapCleanup* cleanup=CTrapCleanup::New();
__ASSERT_ALWAYS(cleanup!=NULL, CSurfaceUpdateServer::PanicServer(EUpdateServPanicNoMemory));
// create an active scheduler and server
CActiveScheduler *pA = new CActiveScheduler;
__ASSERT_ALWAYS(pA != NULL, CSurfaceUpdateServer::PanicServer(EUpdateServPanicNoMemory));
//Install the active scheduler
CActiveScheduler::Install(pA);
CSurfaceUpdateServer *pS = NULL;
TRAPD(err, pS = CSurfaceUpdateServer::NewL());
__ASSERT_ALWAYS(err == KErrNone, CSurfaceUpdateServer::PanicServer(EUpdateServPanicNoMemory));
// Start the server
#ifndef TEST_SURFACE_UPDATE
err = pS->Start(KSurfaceUpdateServerName);
#else
err = pS->Start(KTestSurfaceUpdateServerName);
#endif
if (err != KErrNone)
{
CSurfaceUpdateServer::PanicServer(EUpdateServPanicStartUp);
}
*(static_cast <MSurfaceUpdateServerProvider**> (aAny)) = pS->SurfaceUpdateProvider();
// Let everyone know that we are ready to
// deal with requests.
RThread::Rendezvous(KErrNone);
// And start fielding requests from client(s).
CActiveScheduler::Start();
// Tidy up...
delete pS;
delete pA;
delete cleanup;
return KErrNone;
}
inline TInt CSurfaceUpdateServer::NumUpdateReceivers() const
{
return iUpdateReceiver.Count();
}
/**
Compare two content update receiver entries according to their priority.
The function is used when receiver priority entry is inserted/retrieved from the array (iCompReceiverPriorityOrder).
@return zero, if the priorities of two receivers are equal;
a negative value, if the priority of the first receiver is less than the priority of the second one;
a positive value, if the priority of the first receiver is greater than the priority of the second one.
*/
TInt CSurfaceUpdateServer::CompareUpdateReceiverPriority(const TUpdateReceiverPriorityEntry& aEntry1, const TUpdateReceiverPriorityEntry& aEntry2)
{
return aEntry2.iPriority - aEntry1.iPriority;
}
/**
class CSurfaceUpdateSession
Maintain the channel between clients and the server.
Functions are provided will respond appropriately to client messages.
*/
CSurfaceUpdateSession::CSurfaceUpdateSession(const RPointerArray<TUpdateReceiverPriorityEntry>& aEntryList) :
CSession2(),
iUpdateReceiverEntryList(aEntryList)
{
}
/**
Cancel any outstanding client notification requests.
All resources owned by the session will either be immediately released or
scheduled for deferred deletion.
*/
CSurfaceUpdateSession::~CSurfaceUpdateSession()
{
CancelAllUpdateNotifications();
iUpdateReceiverNotificationBatches.ResetAndDestroy();
}
/**
Main function which deals with requests from clients.
*/
void CSurfaceUpdateSession::ServiceL(const RMessage2& aMessage)
{
switch (aMessage.Function())
{
case EUpdateServNotifyWhenAvailable:
NotifyWhenAvailable(aMessage);
return;
case EUpdateServNotifyWhenDisplayed:
NotifyWhenDisplayed(aMessage);
return;
case EUpdateServNotifyWhenDisplayedXTimes:
NotifyWhenDisplayedXTimes(aMessage);
return;
case EUpdateServSubmitUpdate:
SubmitUpdate(aMessage);
return;
case EUpdateServCancelAllNotifications:
CancelAllUpdateNotifications();
aMessage.Complete(KErrNone);
return;
#ifdef TEST_SURFACE_UPDATE
case EUpdateServOOM:
{
SetHeapFailure(aMessage);
return;
}
#endif
default:
PanicClient(aMessage, EUpdateServPanicBadRequest);
return;
}
}
#ifdef TEST_SURFACE_UPDATE
/**
Simulate heap allocation failure for the SUS thread's heap.
Aim for OOM testing.
*/
void CSurfaceUpdateSession::SetHeapFailure(const RMessage2& aMessage)
{
TInt numElement = iUpdateReceiverNotificationBatches.Count();
TInt index = 0;
while(numElement)
{
CUpdateReceiverNotificationBatch* batch = iUpdateReceiverNotificationBatches[index];
if(batch->iType == EUpdateSrvReusable)
{
iUpdateReceiverNotificationBatches.Remove(index);
delete batch;
}
else
{
index++;
}
numElement--;
}
if(numElement == 0)
{
iUpdateReceiverNotificationBatches.Reset();
}
TInt failRate = aMessage.Int0();
if(!failRate)
{
__UHEAP_RESET;
}
else
{
__UHEAP_SETFAIL(RHeap::EDeterministic, failRate);
}
aMessage.Complete(KErrNone);
}
#endif
void CSurfaceUpdateSession::PanicClient(const RMessage2& aMessage, TInt aPanic) const
{
_LIT(KTxtServer,"SurfUpServ");
aMessage.Panic(KTxtServer, aPanic);
}
/**
Return first inactive spare notification object stored in the notification array,
create a new one if fail to find it in the array.
*/
CUpdateReceiverNotificationBatch* CSurfaceUpdateSession::UpdateReceiverNotificationBatchL()
{
TInt numElement = iUpdateReceiverNotificationBatches.Count();
CUpdateReceiverNotificationBatch* notifier = NULL;
for(TInt index = 0; index < numElement; index++)
{
notifier = iUpdateReceiverNotificationBatches[index];
if(notifier->iType == EUpdateSrvReusable)
{
__ASSERT_ALWAYS(notifier->iMsg.IsNull(), CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity));
if(numElement > index + 1)
{
//to improve a search, append the element to the end of the array
iUpdateReceiverNotificationBatches.Remove(index);
iUpdateReceiverNotificationBatches.AppendL(notifier);
}
return notifier;
}
}
CSurfaceUpdateServer* server = (CSurfaceUpdateServer*) Server();
notifier = new (ELeave) CUpdateReceiverNotificationBatch(this, server->NumUpdateReceivers());
CleanupStack::PushL(notifier);
iUpdateReceiverNotificationBatches.AppendL(notifier);
CleanupStack::Pop();
return notifier;
}
void CSurfaceUpdateSession::NotifyWhenAvailable(const RMessage2& aMessage)
{
StoreNotification(iAvailable, aMessage, EUpdateSrvAvailable);
}
void CSurfaceUpdateSession::NotifyWhenDisplayed(const RMessage2& aMessage)
{
StoreNotification(iDisplayed, aMessage, EUpdateSrvDisplayed);
}
void CSurfaceUpdateSession::NotifyWhenDisplayedXTimes(const RMessage2& aMessage)
{
StoreNotification(iDisplayedXTimes, aMessage, EUpdateSrvDisplayedXTimes);
}
void CSurfaceUpdateSession::StoreNotification(CUpdateReceiverNotificationBatch*& aNotifier, const RMessage2& aMessage, TNotificationType aType)
{
if(aNotifier)
{
if(!aNotifier->iMsg.IsNull())
{//not expected behaviour
//would happen if a client sends the same notification request in a row
IssueRequestComplete(KErrCancel);
PanicClient(aMessage, EUpdateServPanicDataIntegrity);
return;
}
}
else
{
TRAPD(res, aNotifier = UpdateReceiverNotificationBatchL());
if(res != KErrNone)
{
aMessage.Complete(res);
return;
}
}
aNotifier->iMsg = aMessage;
aNotifier->iType=aType;
}
/*
Complete the outstanding requests and reset internal request variables to zero.
@param aErr Error code for completion.
*/
void CSurfaceUpdateSession::IssueRequestComplete(TInt aErr)
{
if(iAvailable)
{
if(!iAvailable->iMsg.IsNull())
{
iAvailable-> iMsg.Complete(aErr);
iAvailable->CheckForReuse();
}
iAvailable = NULL;
}
if(iDisplayed)
{
if(!iDisplayed -> iMsg.IsNull())
{
iDisplayed -> iMsg.Complete(aErr);
iDisplayed->CheckForReuse();
}
iDisplayed = NULL;
}
if(iDisplayedXTimes)
{
if(!iDisplayedXTimes->iMsg.IsNull())
{
iDisplayedXTimes->iMsg.Complete(aErr);
iDisplayedXTimes->CheckForReuse();
}
iDisplayedXTimes=NULL;
}
}
/**
Redirect call to the DoSubmitUpdateL; error handling
*/
void CSurfaceUpdateSession::SubmitUpdate(const RMessage2& aMessage)
{
TInt err = KErrNone;
TRAP(err, DoSubmitUpdateL(aMessage));
if(err != KErrNone)
{
IssueRequestComplete(err);
}
aMessage.Complete(err);
}
/**
Issue request for the update to the composition receiver;
ask notification on composition event if required.
*/
void CSurfaceUpdateSession::DoSubmitUpdateL(const RMessage2& aMessage)
{
TInt displayedXTimes = 0;
TInt screen;
TInt buffer;
TSurfaceId surfaceId;
TPckg<TSurfaceId> surfaceIdPckg(surfaceId);
RRegion *region = NULL;
TRect *rectangleList = NULL;
//extract the composition data
aMessage.ReadL(1, surfaceIdPckg);
screen = aMessage.Int0();
buffer = aMessage.Int2();
//validate parameters
if((screen < 0) || (buffer < 0))
{
User::Leave(KErrArgument);
}
//Check that we haven't used another update method before
//The comparison is slightly optimized for performance as oppose to code size
if(iUpdateMethod == EUpdateMethodGlobal)
{
if(screen != KAllScreens)
User::Leave(KErrNotSupported);
}
else if(iUpdateMethod == EUpdateMethodPerScreen)
{
if(screen == KAllScreens)
User::Leave(KErrNotSupported);
}
TInt len = aMessage.GetDesLength(3);
if(len > 0)
{
rectangleList = (TRect*) (User::AllocL(len));
CleanupStack::PushL(rectangleList);
TPtr8 ptrRect(reinterpret_cast<TUint8*> (rectangleList), len);
aMessage.ReadL(3, ptrRect);
TInt regionCount = len / sizeof(TRect);
region = new (ELeave) RRegion(regionCount, rectangleList);
CleanupStack::PushL(region);
}
if(iDisplayedXTimes)
{
displayedXTimes = iDisplayedXTimes -> iMsg.Int0();
if(displayedXTimes < 1)
{
iDisplayedXTimes->iMsg.Complete(KErrArgument);
iDisplayedXTimes->CheckForReuse();
iDisplayedXTimes = NULL;
}
}
const CSurfaceUpdateServer* server = static_cast <const CSurfaceUpdateServer*> (Server());
if(screen != KAllScreens)
{
MCompositionSurfaceUpdate* receiver = server->UpdateReceiver(screen);
if(!receiver)
{
User::Leave(KErrUpdateReceiverNotAvailable);
}
DispatchUpdate(surfaceId, buffer, region, &displayedXTimes, receiver);
iUpdateMethod = EUpdateMethodPerScreen;
}
else
{
DispatchUpdate(surfaceId, buffer, region, &displayedXTimes);
iUpdateMethod = EUpdateMethodGlobal;
}
if(region)
{
CleanupStack::PopAndDestroy(2, rectangleList);
}
}
/**
Dispatch update to the composition update receivers.
The function could fail to allocate the notification objects
due to memory shortage, however, the content update command will still be submitted to the GCE.
*/
void CSurfaceUpdateSession::DispatchUpdate(const TSurfaceId& aSurfaceId, TInt aBuffer, RRegion* aRegion, TInt* aDisplayedXTimes, MCompositionSurfaceUpdate* aReceiver)
{
TInt index = 1;
TInt numReceivers = 1;
TInt priority = 0;
if(!aReceiver)
{
aReceiver = iUpdateReceiverEntryList[0]->iUpdateReceiver;
priority = iUpdateReceiverEntryList[0]->iPriority;
numReceivers = iUpdateReceiverEntryList.Count(); //get the number of all receivers present in the system
}
for(;;)
{
CUpdateReceiverNotification* receiverNotificationAvailable = NULL;
CUpdateReceiverNotification* receiverNotificationDisplayed = NULL;
CUpdateReceiverNotification* receiverNotificationDisplayedXTimes = NULL;
TUint32* timeStamp = NULL;
//activate objects
if(iAvailable)
{
receiverNotificationAvailable = iAvailable->UpdateReceiverNotification();
if(!receiverNotificationAvailable)
{
iAvailable->iMsg.Complete(KErrNoMemory);
iAvailable->CheckForReuse();
iAvailable = NULL;
}
}
if(iDisplayed)
{
receiverNotificationDisplayed = iDisplayed->UpdateReceiverNotification(priority);
if(!receiverNotificationDisplayed)
{
iDisplayed->iMsg.Complete(KErrNoMemory);
iDisplayed->CheckForReuse();
iDisplayed = NULL;
}
}
if(iDisplayedXTimes)
{
receiverNotificationDisplayedXTimes = iDisplayedXTimes->UpdateReceiverNotification();
if(!receiverNotificationDisplayedXTimes)
{
iDisplayedXTimes->iMsg.Complete(KErrNoMemory);
iDisplayedXTimes->CheckForReuse();
iDisplayedXTimes = NULL;
}
}
TRequestStatus *statusAvailable = NULL;
TRequestStatus *statusDisplayed = NULL;
TRequestStatus *statusDisplayedXTimes = NULL;
//activate all notifications
if(receiverNotificationAvailable)
{
statusAvailable = &(receiverNotificationAvailable->Status());
receiverNotificationAvailable->Activate();
}
if(receiverNotificationDisplayed)
{
statusDisplayed = &(receiverNotificationDisplayed->Status());
timeStamp = & receiverNotificationDisplayed -> iTimeStamp;
receiverNotificationDisplayed->Activate();
}
if(receiverNotificationDisplayedXTimes)
{
statusDisplayedXTimes = &(receiverNotificationDisplayedXTimes->Status());
receiverNotificationDisplayedXTimes->Activate();
}
//And finally, send request to the receiver
aReceiver->ContentUpdated(aSurfaceId, aBuffer, aRegion,
statusAvailable, statusDisplayed, timeStamp,
statusDisplayedXTimes, statusDisplayedXTimes ? aDisplayedXTimes : NULL);
if(numReceivers > index)
{
priority = iUpdateReceiverEntryList[index]->iPriority;
aReceiver = iUpdateReceiverEntryList[index++]->iUpdateReceiver;
}
else
break;
}//for(;;)
iAvailable = NULL;
iDisplayed = NULL;
iDisplayedXTimes = NULL;
}
void CSurfaceUpdateSession::CancelAllUpdateNotifications()
{
//go through all notification objects and issue request complete for outstanding requests
TInt count = iUpdateReceiverNotificationBatches.Count();
for(TInt index = 0; index < count; index++)
{
CUpdateReceiverNotificationBatch* notifier = iUpdateReceiverNotificationBatches[index];
if(!notifier->iMsg.IsNull())
{
notifier->iMsg.Complete(KErrCancel);
}
}
iAvailable = NULL;
iDisplayed = NULL;
iDisplayedXTimes = NULL;
}
//*********************
CUpdateReceiverNotification::CUpdateReceiverNotification(CActive::TPriority aPriority, TInt aReceiverPriority, CUpdateReceiverNotificationBatch *aParentNotificationBatch) :
CActive(aPriority), iUpdateReceiverPriority(aReceiverPriority), iParentNotificationBatch(aParentNotificationBatch)
{
CActiveScheduler::Add(this);
#ifdef TEST_SURFACE_UPDATE
iServer = iParentNotificationBatch->Server();
#endif
}
CUpdateReceiverNotification::~CUpdateReceiverNotification()
{
//Content updates can not be cancelled. The user must ensure that the associated content update
//is complete before deleting this object.
__ASSERT_ALWAYS(!IsActive(), CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity));
}
void CUpdateReceiverNotification::DoCancel()
{
//Content Updates can not be cancelled. Content Updates must be allowed to complete normally.
CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity);
}
/**
Is triggered every time the GCE signals on notification objects.
Delegates processing to the parent notification batch.
*/
void CUpdateReceiverNotification::RunL()
{
// CReceiverNotification object is deleted here once it is set to self destroying when run.
if (iSelfDestructWhenRun)
{
#ifdef TEST_SURFACE_UPDATE
DecNumberPendingNotifications();
#endif
delete this;
return;
}
iParentNotificationBatch->ProcessNotificationEvent(this);
}
TRequestStatus& CUpdateReceiverNotification::Status()
{
return iStatus;
}
void CUpdateReceiverNotification::Activate()
{
iStatus = KRequestPending;
SetActive();
}
#ifdef TEST_SURFACE_UPDATE
void CUpdateReceiverNotification::DecNumberPendingNotifications()
{
if (iServer)
{
iServer-> iNumberPendingNotification--;
}
}
#endif
/**
The class maintains the notification objects of the same type and attributed to a
particular submit update command.
*/
CUpdateReceiverNotificationBatch::CUpdateReceiverNotificationBatch(CSurfaceUpdateSession *aSession, TInt aNumReceivers) :
iSession(aSession), iNumUpdateReceivers(aNumReceivers)
{
CheckForReuse();
}
CUpdateReceiverNotificationBatch::~CUpdateReceiverNotificationBatch()
{
TInt count = iUpdateReceiverNotifications.Count();
for(TInt index = 0; index < count; index++)
{
CUpdateReceiverNotification* notifier = iUpdateReceiverNotifications[index];
// CReceiverNotification active object cannot be destroyed if it is active and waiting for
// receiver to complete the notification. It deletes itself inside its RunL function when the
// notification is completed.
if (notifier->IsActive())
{
notifier->iSelfDestructWhenRun = ETrue;
#ifdef TEST_SURFACE_UPDATE
IncNumberPendingNotifications();
#endif
}
else
{
delete notifier;
}
}
iUpdateReceiverNotifications.Close();
}
/**
The function processes signal from composition receiver.
It completes outstanding messages on the client and set timestamp parameters if appropriate.
Note that this object will not be removed from the
array (CSurfaceUpdateSession:: iReceiverNotifications) and can be reused later.
*/
void CUpdateReceiverNotificationBatch::ProcessNotificationEvent(CUpdateReceiverNotification* aReceiverNotification)
{
TInt index = iUpdateReceiverNotifications.Find(aReceiverNotification);
__ASSERT_DEBUG(index != KErrNotFound, CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity));
iUpdateReceiverNotifications.Remove(index);
if(iMsg.IsNull())
{
CheckForReuse();
delete aReceiverNotification;
return; // the message has already been completed, possibly, because of request cancelation
}
TBool oldCompleteWithSuccess = iCompleteWithSuccess;
TBool newCompleteWithSuccess = EFalse;
if(aReceiverNotification->iStatus.Int() == KErrNone)
{
iCompleteWithSuccess = ETrue;
newCompleteWithSuccess = ETrue;
}
switch(iType)
{
case EUpdateSrvAvailable:
if(iUpdateReceiverNotifications.Count() == 0)
{
TInt res = iCompleteWithSuccess ? KErrNone : aReceiverNotification->iStatus.Int();
iMsg.Complete(res);
}
break;
case EUpdateSrvDisplayedXTimes:
if((newCompleteWithSuccess && (index == 0)) || (iUpdateReceiverNotifications.Count() == 0))
{
TInt res = iCompleteWithSuccess ? KErrNone : aReceiverNotification->iStatus.Int();
iMsg.Complete(res);
}
break;
case EUpdateSrvDisplayed:
if(newCompleteWithSuccess &&
((iHighestPriority < aReceiverNotification->iUpdateReceiverPriority) || !oldCompleteWithSuccess))
{
iHighestPriority = aReceiverNotification->iUpdateReceiverPriority;
iTimeStamp = aReceiverNotification->iTimeStamp;
}
if((newCompleteWithSuccess && (index == 0)) || (iUpdateReceiverNotifications.Count() == 0))
{
TPckgBuf<TUint32> p(iTimeStamp);
TInt res = iMsg.Write(0, p); //We must not leave here as it will cause panic on Active scheduler. This would be inappropriate for the server side thread.
if(res == KErrNone)
{
res = iCompleteWithSuccess ? KErrNone : aReceiverNotification->iStatus.Int();
}
iMsg.Complete(res);
}
break;
default:
break;
}
delete aReceiverNotification;
CheckForReuse();
}
/**
The method allocates a notification object and inserts it into the list
*/
CUpdateReceiverNotification* CUpdateReceiverNotificationBatch::UpdateReceiverNotification(TInt aReceiverPriority)
{
__ASSERT_DEBUG(iNumUpdateReceivers > iUpdateReceiverNotifications.Count(), CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity));
CUpdateReceiverNotification* receiverNotification = new CUpdateReceiverNotification(CActive::EPriorityStandard, aReceiverPriority, this);
if(receiverNotification)
{
TInt res = iUpdateReceiverNotifications.Append(receiverNotification);
if(res != KErrNone)
{
delete receiverNotification;
receiverNotification = NULL;
}
}
return receiverNotification;
}
/**
If notification list is empty mark the batch for further use
*/
void CUpdateReceiverNotificationBatch::CheckForReuse()
{
if(iUpdateReceiverNotifications.Count() == 0)
{
iType = EUpdateSrvReusable;
iCompleteWithSuccess = EFalse;
iHighestPriority = 0;
iTimeStamp = 0;
}
}
#ifdef TEST_SURFACE_UPDATE
CSurfaceUpdateServer* CUpdateReceiverNotificationBatch::Server()
{
return (CSurfaceUpdateServer*)(iSession->Server());
}
void CUpdateReceiverNotificationBatch::IncNumberPendingNotifications()
{
CServer2* server = static_cast<CServer2*> (Server());
if(server)
{
(static_cast<CSurfaceUpdateServer*> (server))-> iNumberPendingNotification++;
}
}
#endif
/**
The class will be used by composition receiver
*/
CSurfaceUpdateServerProvider* CSurfaceUpdateServerProvider::NewL(CActive::TPriority aPriority, CSurfaceUpdateServer* aServer)
{
CSurfaceUpdateServerProvider* serverProvider = new (ELeave) CSurfaceUpdateServerProvider(aPriority, aServer);
CleanupStack::PushL(serverProvider);
serverProvider->ConstructL();
CleanupStack::Pop();
return serverProvider;
}
CSurfaceUpdateServerProvider::CSurfaceUpdateServerProvider(CActive::TPriority aPriority, CSurfaceUpdateServer* aServer) :
CActive(aPriority), iServer(aServer)
{
RThread thread;
iThreadId = thread.Id();
}
CSurfaceUpdateServerProvider::~CSurfaceUpdateServerProvider()
{
iSemaphore.Close();
Cancel();
}
/**
Create provider and add it to the active scheduler
*/
void CSurfaceUpdateServerProvider::ConstructL()
{
User::LeaveIfError(iSemaphore.CreateLocal(0));
CActiveScheduler::Add(this);
iStatus = KRequestPending;
SetActive();
}
/**
Assign composition receiver instance to particular screen.
The following calls of this function for the same screen will override the previous.
@see MSurfaceUpdateServerProvider::::Register
*/
EXPORT_C TInt CSurfaceUpdateServerProvider::Register(TInt aScreen, CBase* aReceiver, TInt aPriority)
{
RThread thread;
TInt res = thread.Open(iThreadId);
if(res == KErrNone)
{
iScreen = aScreen;
iUpdateReceiver = aReceiver;
iPriority = aPriority;
TRequestStatus* status = &iStatus;
thread.RequestComplete(status, EUpdateServEventRegister);
thread.Close();
iSemaphore.Wait();
res = iRegisterErr;
}
return res;
}
/**
Terminate the SUS and release all memory associated with it.
The function was introduced for debugging purpose only and is not considered
as part of public API.
*/
EXPORT_C void CSurfaceUpdateServerProvider::Terminate()
{
#ifdef TEST_SURFACE_UPDATE
RThread thread;
if(thread.Open(iThreadId) == KErrNone)
{
if (iServer)
{
while((static_cast<CSurfaceUpdateServer*> (iServer))-> iNumberPendingNotification)
User::After(TTimeIntervalMicroSeconds32(1000));
}
TRequestStatus* status = &iStatus;
thread.RequestComplete(status, EUpdateServEventTerminate);
//wait until the thread dies
TRequestStatus status1;
thread.Logon(status1);
User::WaitForRequest(status1);
thread.Close();
Dll::SetTls(NULL);
}
#endif
}
/**
The function processes signals which come from composition receiver thread.
*/
void CSurfaceUpdateServerProvider::RunL()
{
switch(iStatus.Int())
{
case EUpdateServEventTerminate:
CActiveScheduler::Stop();
return;
case EUpdateServEventRegister:
iRegisterErr = iServer->Register(iScreen, iUpdateReceiver, iPriority);
iUpdateReceiver = NULL;
iStatus = KRequestPending;
SetActive();
iSemaphore.Signal();
break;
default :
CSurfaceUpdateServer::PanicServer(EUpdateServPanicBadRequest);
iStatus = KRequestPending;
SetActive();
break;
}
}
void CSurfaceUpdateServerProvider::DoCancel()
{
}
/**
Spawn a thread within WSERV process. This will lead to starting the surface update server in it
@publishedPartner
@prototype Intended to be used by Surface Update control flow
@param aUpdateProvider - [out] reference pointer to surface update server provider,
which will be set when the server is started. The variable will be used to registry
composition receiver instances. The caller doesn't acquire the ownership of this instance,
thus mustn't delete it. The pointer will be valid until server is operating,
i.e. system is up.
@return KErrNone if an operation is successful, any other system error codes otherwise
*/
EXPORT_C TInt StartSurfaceUpdateServer(MSurfaceUpdateServerProvider*& aSurfaceUpdateServerProvider)
{
#ifndef TEST_SURFACE_UPDATE
TPtrC serverName(KSurfaceUpdateServerName);
#else
TPtrC serverName(KTestSurfaceUpdateServerName);
#endif
TAny *provider = Dll::Tls();
if(provider)
{
aSurfaceUpdateServerProvider = static_cast <MSurfaceUpdateServerProvider*> (provider);
return KErrNone;
}
TFullName name;
RThread serverThread;
TInt res = KErrAlreadyExists;
TFindServer findSurfaceUpdateServer(serverName);
// Need to check that the server exists.
if (findSurfaceUpdateServer.Next(name) !=KErrNone)
{
TTime tm;
TBuf<32> buf;
tm.UniversalTime();
TRAP(res, tm.FormatL(buf, _L("_%H%T%S%C")));
if(res != KErrNone)
{
return res;
}
TBuf<128> threadName(serverName);
threadName.Append(buf); //guarantee uniqueness of the thread name
// Create the thread for the server.
res = serverThread.Create(threadName,
CSurfaceUpdateServer::ThreadFunction,
KDefaultStackSize,
KDefaultHeapSize,
KDefaultHeapSize,
(TAny*) &aSurfaceUpdateServerProvider
);
// The thread has been created OK so get it started - however
// we need to make sure that it has started before we continue.
if (res==KErrNone)
{
TRequestStatus rendezvousStatus;
TThreadPriority priority = RThread().Priority();
serverThread.SetPriority(priority); // The same as the priority of the creating thread
serverThread.Rendezvous(rendezvousStatus);
serverThread.Resume();
User::WaitForRequest(rendezvousStatus);
res = rendezvousStatus.Int();
Dll::SetTls(aSurfaceUpdateServerProvider);
}
// The thread has not been created - clearly there's been a problem.
else
{
serverThread.Close();
}
}
return res;
}