graphicscomposition/surfaceupdate/src/surfaceupdateserver.cpp
changeset 0 5d03bc08d59c
child 97 0e9202c0340c
child 98 bf7481649c98
equal deleted inserted replaced
-1:000000000000 0:5d03bc08d59c
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "surfaceupdateserver.h"
       
    17 
       
    18 #include <graphics/compositionsurfaceupdate.h>
       
    19 #include <graphics/extensioncontainer.h>
       
    20 
       
    21 #include "surfaceupdate.h"
       
    22 #include "suerror.h"
       
    23 #ifdef TEST_SURFACE_UPDATE
       
    24 #include "surfaceupdatetest.h"
       
    25 #endif
       
    26 
       
    27 
       
    28 
       
    29 const TUint KDefaultHeapSize=0x10000;
       
    30 
       
    31 
       
    32 /**
       
    33 The server maintains session with the clients. 
       
    34 It starts during the initialization of the Content update receiver thread.  
       
    35 */
       
    36 CSurfaceUpdateServer* CSurfaceUpdateServer::NewL()
       
    37 	{
       
    38 	CSurfaceUpdateServer* server = new (ELeave) CSurfaceUpdateServer(EPriorityStandard);
       
    39 	CleanupStack::PushL(server);
       
    40 	server->ConstructL();
       
    41 	CleanupStack::Pop();
       
    42 	return server;	
       
    43 	}
       
    44 	
       
    45 CSurfaceUpdateServer::CSurfaceUpdateServer(CActive::TPriority aPriority) :
       
    46 	CServer2(aPriority)
       
    47 	{
       
    48 	}
       
    49 	
       
    50 CSurfaceUpdateServer::~CSurfaceUpdateServer()
       
    51 	{
       
    52 	iUpdateReceiver.Close();
       
    53 	iUpdateReceiverPriorityOrder.ResetAndDestroy();
       
    54 	delete iServerProvider;
       
    55 	}	
       
    56 	
       
    57 void CSurfaceUpdateServer::ConstructL()
       
    58 	{
       
    59 	iServerProvider = CSurfaceUpdateServerProvider::NewL(EPriorityStandard, this);
       
    60 	}
       
    61 	
       
    62 /**
       
    63 Assign Content update receiver instance to particular screen. 
       
    64 The following calls of this function will override the previous. 
       
    65 
       
    66 @see MSurfaceUpdateServerProvider::Register
       
    67 */	
       
    68 TInt CSurfaceUpdateServer::Register(TInt aScreen, CBase* aUpdateReceiver, TInt aPriority)
       
    69 	{
       
    70 	if(aScreen < 0)
       
    71 		{
       
    72 		return KErrArgument;
       
    73 		}
       
    74 		
       
    75 	TInt err = KErrNone;
       
    76 	while((iUpdateReceiver.Count() <= aScreen) && (err == KErrNone))
       
    77 		{
       
    78 		err = iUpdateReceiver.Append(NULL);
       
    79 		}
       
    80 	
       
    81 	if(err == KErrNone)
       
    82 		{
       
    83 		TUpdateReceiverPriorityEntry *receiverPriorityEntry = NULL; 
       
    84 		if(!aUpdateReceiver)
       
    85 			{//Client wants to unregister the Content update receiver which has been associated with the given screen number.
       
    86 			MCompositionSurfaceUpdate* receiver = iUpdateReceiver[aScreen];
       
    87 			if(receiver)
       
    88 				{
       
    89 				TInt num = iUpdateReceiverPriorityOrder.Count() - 1;
       
    90 				for(;;)
       
    91 					{//Content update receiver must be in priority list, therefore we don't need to check num >= 0
       
    92 					receiverPriorityEntry = iUpdateReceiverPriorityOrder[num];
       
    93 					if(receiverPriorityEntry->iUpdateReceiver == receiver)
       
    94 						{//remove the Content update receiver from the priority list
       
    95 						iUpdateReceiverPriorityOrder.Remove(num);
       
    96 						delete receiverPriorityEntry;
       
    97 						break;
       
    98 						}
       
    99 					num--;
       
   100 					}
       
   101 				iUpdateReceiver[aScreen] = NULL; //unregister the Content update receiver by setting the entry to NULL
       
   102 				}
       
   103 			}
       
   104 		else
       
   105 			{
       
   106 	        CExtensionContainer* extCont=static_cast<CExtensionContainer*>(aUpdateReceiver);
       
   107 	        MCompositionSurfaceUpdate* updateReceiver=extCont->GetInterface<MCompositionSurfaceUpdate>();
       
   108 	        if (updateReceiver)
       
   109 	            {
       
   110                 receiverPriorityEntry = new TUpdateReceiverPriorityEntry;
       
   111                 if(!receiverPriorityEntry)
       
   112                     return KErrNoMemory;
       
   113                 receiverPriorityEntry->iPriority = aPriority;
       
   114                 receiverPriorityEntry->iUpdateReceiver = updateReceiver;
       
   115         
       
   116                 err = iUpdateReceiverPriorityOrder.InsertInOrder(receiverPriorityEntry, CSurfaceUpdateServer::CompareUpdateReceiverPriority);
       
   117                 if(err == KErrNone)
       
   118                     {
       
   119                     iUpdateReceiver[aScreen] = updateReceiver;
       
   120                     }
       
   121                 else
       
   122                     {
       
   123                     delete receiverPriorityEntry;
       
   124                     }
       
   125 	            }
       
   126 	        else
       
   127 	            {
       
   128 	            err=KErrArgument;
       
   129 	            }
       
   130 			}	
       
   131 		}
       
   132 	return err;
       
   133 	}
       
   134 
       
   135 /**
       
   136 Create a new session with a server. Derived from CSession2.
       
   137 
       
   138 @param aVersion required version of the server
       
   139 @param aMessage message from the client.
       
   140 @return New instance of the CSurfaceUpdateSession.
       
   141 */
       
   142 CSession2* CSurfaceUpdateServer::NewSessionL(const TVersion& aVersion, const RMessage2& /*aMessage*/) const
       
   143 	{
       
   144       // Check that the version is OK
       
   145 	TVersion v(KSurfaceUpdateServMajorVersionNumber,KSurfaceUpdateServMinorVersionNumber,KSurfaceUpdateServBuildVersionNumber);
       
   146 	if (!User::QueryVersionSupported(v,aVersion))
       
   147 		User::Leave(KErrNotSupported);	
       
   148 	
       
   149 	// Security is not an issue, any client can have connection to the server	
       
   150 	// Create the session.
       
   151 	return new (ELeave) CSurfaceUpdateSession(UpdateReceiverPriority());
       
   152 	}
       
   153 
       
   154 /**
       
   155 Utility function to panic the server
       
   156 
       
   157 @param aPanic Panic code
       
   158 */
       
   159 void CSurfaceUpdateServer::PanicServer(TSurfaceUpdateServPanic aPanic)
       
   160 	{
       
   161 	_LIT(KTxtServerPanic,"Surface Update Server panic");
       
   162 	User::Panic(KTxtServerPanic, aPanic);
       
   163 	}
       
   164 
       
   165 /**
       
   166 Provide composition receiver.
       
   167 
       
   168 @param aScreen targeted screen
       
   169 @return Composition receiver object, associated to particular screen
       
   170 */
       
   171 MCompositionSurfaceUpdate* CSurfaceUpdateServer::UpdateReceiver(TInt aScreen) const
       
   172 	{
       
   173 	if(aScreen >= iUpdateReceiver.Count())//negative value won't reach this point 
       
   174 		return NULL;
       
   175 	return iUpdateReceiver[aScreen];	
       
   176 	}
       
   177 
       
   178 TInt CSurfaceUpdateServer::ThreadFunction(TAny* aAny)
       
   179 	{
       
   180 	  // get clean-up stack
       
   181 	CTrapCleanup* cleanup=CTrapCleanup::New();
       
   182 	__ASSERT_ALWAYS(cleanup!=NULL, CSurfaceUpdateServer::PanicServer(EUpdateServPanicNoMemory));
       
   183 	
       
   184 	  // create an active scheduler and server
       
   185 	CActiveScheduler *pA = new CActiveScheduler;
       
   186 	__ASSERT_ALWAYS(pA != NULL, CSurfaceUpdateServer::PanicServer(EUpdateServPanicNoMemory));
       
   187 
       
   188 	  //Install the active scheduler
       
   189 	CActiveScheduler::Install(pA);
       
   190 
       
   191 	CSurfaceUpdateServer *pS = NULL;
       
   192 	TRAPD(err, pS = CSurfaceUpdateServer::NewL());
       
   193 	__ASSERT_ALWAYS(err == KErrNone, CSurfaceUpdateServer::PanicServer(EUpdateServPanicNoMemory));
       
   194 	
       
   195 	  // Start the server
       
   196 #ifndef TEST_SURFACE_UPDATE
       
   197 	err = pS->Start(KSurfaceUpdateServerName);
       
   198 #else
       
   199 	err = pS->Start(KTestSurfaceUpdateServerName);
       
   200 #endif
       
   201 	
       
   202 	if (err != KErrNone)
       
   203 		{
       
   204 		CSurfaceUpdateServer::PanicServer(EUpdateServPanicStartUp);
       
   205 		}
       
   206 	*(static_cast <MSurfaceUpdateServerProvider**> (aAny)) = pS->SurfaceUpdateProvider();
       
   207       
       
   208       // Let everyone know that we are ready to
       
   209       // deal with requests.
       
   210 	RThread::Rendezvous(KErrNone);
       
   211 	  // And start fielding requests from client(s).
       
   212 	CActiveScheduler::Start();
       
   213 
       
   214      // Tidy up... 	
       
   215 	delete pS;
       
   216 	delete pA;
       
   217 	delete cleanup; 
       
   218 	
       
   219 	return KErrNone;
       
   220 	}
       
   221 
       
   222 inline TInt CSurfaceUpdateServer::NumUpdateReceivers() const
       
   223 	{
       
   224 	return iUpdateReceiver.Count(); 
       
   225 	}
       
   226 
       
   227 /**
       
   228 Compare two content update receiver entries according to their priority. 
       
   229 The function is used when receiver priority entry is inserted/retrieved from the array (iCompReceiverPriorityOrder).
       
   230 
       
   231 @return zero, if the priorities of two receivers are equal; 
       
   232 a negative value, if the priority of the first receiver is less than the priority of the second one; 
       
   233 a positive value, if the priority of the first receiver is greater than the priority of the second one.
       
   234 */
       
   235 TInt CSurfaceUpdateServer::CompareUpdateReceiverPriority(const TUpdateReceiverPriorityEntry& aEntry1, const TUpdateReceiverPriorityEntry& aEntry2)
       
   236 	{
       
   237 	return aEntry2.iPriority - aEntry1.iPriority;
       
   238 	}
       
   239 
       
   240 /**
       
   241 class CSurfaceUpdateSession
       
   242 
       
   243 Maintain the channel between clients and the server. 
       
   244 Functions are provided will respond appropriately to client messages. 
       
   245 */
       
   246 
       
   247 CSurfaceUpdateSession::CSurfaceUpdateSession(const RPointerArray<TUpdateReceiverPriorityEntry>& aEntryList) :
       
   248 	CSession2(),
       
   249 	iUpdateReceiverEntryList(aEntryList)
       
   250 	{
       
   251 	}
       
   252 
       
   253 /**
       
   254 Cancel any outstanding client notification requests. 
       
   255 All resources owned by the session will either be immediately released or 
       
   256 scheduled for deferred deletion.
       
   257 */
       
   258 CSurfaceUpdateSession::~CSurfaceUpdateSession()
       
   259 	{
       
   260 	CancelAllUpdateNotifications();
       
   261 	iUpdateReceiverNotificationBatches.ResetAndDestroy();
       
   262 	}
       
   263 
       
   264 /**
       
   265 Main function which deals with requests from clients.
       
   266 */	
       
   267 void CSurfaceUpdateSession::ServiceL(const RMessage2& aMessage)
       
   268 	{
       
   269 	switch (aMessage.Function())
       
   270 		{
       
   271 	case EUpdateServNotifyWhenAvailable:
       
   272 		NotifyWhenAvailable(aMessage);
       
   273 		return;
       
   274 	case EUpdateServNotifyWhenDisplayed:
       
   275 		NotifyWhenDisplayed(aMessage);
       
   276 		return;
       
   277 	case EUpdateServNotifyWhenDisplayedXTimes:
       
   278 		NotifyWhenDisplayedXTimes(aMessage);
       
   279 		return;
       
   280 	case EUpdateServSubmitUpdate:
       
   281 		SubmitUpdate(aMessage);
       
   282 		return;
       
   283 	case EUpdateServCancelAllNotifications:
       
   284 		CancelAllUpdateNotifications();
       
   285 		aMessage.Complete(KErrNone);
       
   286 		return;
       
   287 #ifdef TEST_SURFACE_UPDATE
       
   288 	case EUpdateServOOM:
       
   289 		{
       
   290 		SetHeapFailure(aMessage);
       
   291 		return;
       
   292 		}
       
   293 #endif
       
   294 	default:
       
   295 		PanicClient(aMessage, EUpdateServPanicBadRequest);
       
   296 		return;
       
   297 		}	 
       
   298 	}
       
   299 
       
   300 #ifdef TEST_SURFACE_UPDATE
       
   301 /**
       
   302   Simulate heap allocation failure for the SUS thread's heap.
       
   303   Aim for OOM testing.
       
   304  */
       
   305 void CSurfaceUpdateSession::SetHeapFailure(const RMessage2& aMessage)
       
   306 	{
       
   307 	TInt numElement = iUpdateReceiverNotificationBatches.Count();
       
   308 	TInt index = 0;
       
   309 	while(numElement)
       
   310 		{
       
   311 		CUpdateReceiverNotificationBatch* batch = iUpdateReceiverNotificationBatches[index];
       
   312 		if(batch->iType == EUpdateSrvReusable)
       
   313 			{
       
   314 			iUpdateReceiverNotificationBatches.Remove(index);
       
   315 			delete batch;
       
   316 			}
       
   317 		else
       
   318 			{
       
   319 			index++;
       
   320 			}
       
   321 		numElement--;            
       
   322 		}
       
   323 	if(numElement == 0)
       
   324 		{
       
   325 		iUpdateReceiverNotificationBatches.Reset();
       
   326 		}
       
   327 	TInt failRate = aMessage.Int0();
       
   328 	if(!failRate)
       
   329 		{
       
   330 		__UHEAP_RESET;
       
   331 		}
       
   332 	else
       
   333 		{
       
   334 		__UHEAP_SETFAIL(RHeap::EDeterministic, failRate);
       
   335 		}
       
   336 	aMessage.Complete(KErrNone);
       
   337 	}
       
   338 #endif
       
   339 
       
   340 void CSurfaceUpdateSession::PanicClient(const RMessage2& aMessage, TInt aPanic) const
       
   341 	{
       
   342 	_LIT(KTxtServer,"SurfUpServ");
       
   343 	aMessage.Panic(KTxtServer, aPanic);
       
   344 	}
       
   345 
       
   346 /**
       
   347 Return first inactive spare notification object stored in the notification array, 
       
   348 create a new one if fail to find it in the array.
       
   349 */	
       
   350 CUpdateReceiverNotificationBatch* CSurfaceUpdateSession::UpdateReceiverNotificationBatchL()
       
   351 	{
       
   352 	TInt numElement = iUpdateReceiverNotificationBatches.Count();
       
   353 	CUpdateReceiverNotificationBatch* notifier = NULL;
       
   354 	for(TInt index = 0; index < numElement; index++)
       
   355 		{
       
   356 		notifier = iUpdateReceiverNotificationBatches[index];
       
   357 		if(notifier->iType == EUpdateSrvReusable)
       
   358 			{
       
   359 			__ASSERT_ALWAYS(notifier->iMsg.IsNull(), CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity));
       
   360 			if(numElement > index + 1)
       
   361 				{
       
   362 			//to improve a search, append the element to the end of the array
       
   363 				iUpdateReceiverNotificationBatches.Remove(index);
       
   364 				iUpdateReceiverNotificationBatches.AppendL(notifier);
       
   365 				}
       
   366 			return notifier;
       
   367 			}
       
   368 		}
       
   369 	
       
   370 	CSurfaceUpdateServer* server = (CSurfaceUpdateServer*) Server();
       
   371 	notifier = new (ELeave) CUpdateReceiverNotificationBatch(this, server->NumUpdateReceivers());
       
   372 	CleanupStack::PushL(notifier);
       
   373 	iUpdateReceiverNotificationBatches.AppendL(notifier);
       
   374 	CleanupStack::Pop();
       
   375 	
       
   376 	return notifier;
       
   377 	}
       
   378 	
       
   379 void CSurfaceUpdateSession::NotifyWhenAvailable(const RMessage2& aMessage)
       
   380 	{
       
   381 	StoreNotification(iAvailable, aMessage, EUpdateSrvAvailable);
       
   382 	}
       
   383 	
       
   384 void CSurfaceUpdateSession::NotifyWhenDisplayed(const RMessage2& aMessage)
       
   385 	{
       
   386 	StoreNotification(iDisplayed, aMessage, EUpdateSrvDisplayed);
       
   387 	}
       
   388 	
       
   389 void CSurfaceUpdateSession::NotifyWhenDisplayedXTimes(const RMessage2& aMessage)
       
   390 	{
       
   391 	StoreNotification(iDisplayedXTimes, aMessage, EUpdateSrvDisplayedXTimes);
       
   392 	}
       
   393 
       
   394 void CSurfaceUpdateSession::StoreNotification(CUpdateReceiverNotificationBatch*& aNotifier, const RMessage2& aMessage, TNotificationType aType)
       
   395 	{
       
   396 	if(aNotifier)
       
   397 		{
       
   398 		if(!aNotifier->iMsg.IsNull())
       
   399 			{//not expected behaviour
       
   400 			//would happen if a client sends the same notification request in a row
       
   401 			IssueRequestComplete(KErrCancel);
       
   402 			PanicClient(aMessage, EUpdateServPanicDataIntegrity);
       
   403 			return;
       
   404 			}
       
   405 		}
       
   406 	else
       
   407 		{
       
   408 		TRAPD(res, aNotifier = UpdateReceiverNotificationBatchL());
       
   409 		if(res != KErrNone)
       
   410 			{
       
   411 			aMessage.Complete(res);
       
   412 			return;
       
   413 			}
       
   414 		}	
       
   415 	
       
   416 	aNotifier->iMsg = aMessage;
       
   417 	aNotifier->iType=aType;
       
   418 	}
       
   419 
       
   420 /*
       
   421 Complete the outstanding requests and reset internal request variables to zero.
       
   422 @param aErr Error code for completion.   
       
   423 */ 	
       
   424 void CSurfaceUpdateSession::IssueRequestComplete(TInt aErr)
       
   425 	{
       
   426 	if(iAvailable)
       
   427 		{
       
   428 		if(!iAvailable->iMsg.IsNull())
       
   429 			{
       
   430 			iAvailable-> iMsg.Complete(aErr);
       
   431 			iAvailable->CheckForReuse();	 
       
   432 			}
       
   433 		iAvailable = NULL;
       
   434 		}
       
   435 
       
   436 	if(iDisplayed)
       
   437 		{
       
   438 		if(!iDisplayed -> iMsg.IsNull())
       
   439 			{
       
   440 			iDisplayed -> iMsg.Complete(aErr);
       
   441 			iDisplayed->CheckForReuse();
       
   442 			}
       
   443 		iDisplayed = NULL;
       
   444 		}
       
   445 
       
   446 	if(iDisplayedXTimes)
       
   447 		{
       
   448 		if(!iDisplayedXTimes->iMsg.IsNull())
       
   449 			{
       
   450 			iDisplayedXTimes->iMsg.Complete(aErr);
       
   451 			iDisplayedXTimes->CheckForReuse();
       
   452 			}
       
   453 		iDisplayedXTimes=NULL;
       
   454 		}
       
   455 	}
       
   456 
       
   457 /**
       
   458 Redirect call to the DoSubmitUpdateL; error handling
       
   459 */
       
   460 void CSurfaceUpdateSession::SubmitUpdate(const RMessage2& aMessage)
       
   461 	{
       
   462 	TInt err = KErrNone;
       
   463 	TRAP(err, DoSubmitUpdateL(aMessage));
       
   464 	if(err != KErrNone)
       
   465 		{
       
   466 		IssueRequestComplete(err);
       
   467 		}
       
   468 	aMessage.Complete(err);	
       
   469 	}
       
   470 
       
   471 /**
       
   472 Issue request for the update to the composition receiver; 
       
   473 ask notification on composition event if required.
       
   474 */	
       
   475 void CSurfaceUpdateSession::DoSubmitUpdateL(const RMessage2& aMessage)
       
   476 	{
       
   477 	TInt displayedXTimes = 0;
       
   478 	TInt screen;
       
   479 	TInt buffer;
       
   480 	TSurfaceId surfaceId;
       
   481 	TPckg<TSurfaceId> surfaceIdPckg(surfaceId);
       
   482 
       
   483 	RRegion *region = NULL;
       
   484 	TRect *rectangleList = NULL;
       
   485 
       
   486 	//extract the composition data
       
   487 	aMessage.ReadL(1, surfaceIdPckg);
       
   488 	
       
   489 	screen = aMessage.Int0();
       
   490 	buffer = aMessage.Int2();
       
   491 	
       
   492 	//validate parameters
       
   493 	if((screen < 0) || (buffer < 0))
       
   494 		{
       
   495 		User::Leave(KErrArgument);
       
   496 		}
       
   497 	//Check that we haven't used another update method before
       
   498 	//The comparison is slightly optimized for performance as oppose to code size
       
   499 	if(iUpdateMethod == EUpdateMethodGlobal) 
       
   500 		{
       
   501 		if(screen != KAllScreens)
       
   502 			User::Leave(KErrNotSupported);
       
   503 		}
       
   504 	else if(iUpdateMethod == EUpdateMethodPerScreen) 
       
   505 		{
       
   506 		if(screen == KAllScreens)
       
   507 			User::Leave(KErrNotSupported);
       
   508 		}
       
   509 	
       
   510 	TInt len = aMessage.GetDesLength(3);
       
   511 	if(len > 0)
       
   512 		{
       
   513 		rectangleList = (TRect*) (User::AllocL(len));
       
   514 		CleanupStack::PushL(rectangleList);
       
   515 		TPtr8 ptrRect(reinterpret_cast<TUint8*> (rectangleList), len);
       
   516 		aMessage.ReadL(3, ptrRect);
       
   517 		TInt regionCount = len / sizeof(TRect);
       
   518 		region = new (ELeave) RRegion(regionCount, rectangleList);
       
   519 		CleanupStack::PushL(region);
       
   520 		}
       
   521 	if(iDisplayedXTimes)
       
   522 		{
       
   523 		displayedXTimes = iDisplayedXTimes -> iMsg.Int0();
       
   524 		if(displayedXTimes < 1)
       
   525 			{
       
   526 			iDisplayedXTimes->iMsg.Complete(KErrArgument);
       
   527 			iDisplayedXTimes->CheckForReuse();
       
   528 			iDisplayedXTimes = NULL;
       
   529 			}
       
   530 		}
       
   531 	
       
   532 	const CSurfaceUpdateServer* server = static_cast <const CSurfaceUpdateServer*> (Server());	
       
   533 	if(screen != KAllScreens)
       
   534 		{
       
   535 		MCompositionSurfaceUpdate* receiver = server->UpdateReceiver(screen);
       
   536 		if(!receiver)
       
   537 			{
       
   538 			User::Leave(KErrUpdateReceiverNotAvailable);
       
   539 			}
       
   540 		DispatchUpdate(surfaceId, buffer, region, &displayedXTimes, receiver);
       
   541 		iUpdateMethod = EUpdateMethodPerScreen;
       
   542 		}
       
   543 	else
       
   544 		{
       
   545 		DispatchUpdate(surfaceId, buffer, region, &displayedXTimes);
       
   546 		iUpdateMethod = EUpdateMethodGlobal;
       
   547 		}
       
   548 				
       
   549 	if(region)
       
   550 		{
       
   551 		CleanupStack::PopAndDestroy(2, rectangleList);
       
   552 		}
       
   553 	}
       
   554 
       
   555 /**
       
   556  Dispatch update to the composition update receivers. 
       
   557  The function could fail to allocate the notification objects 
       
   558  due to memory shortage, however, the content update command will still be submitted to the GCE.  
       
   559  */
       
   560 void CSurfaceUpdateSession::DispatchUpdate(const TSurfaceId& aSurfaceId, TInt aBuffer, RRegion* aRegion, TInt* aDisplayedXTimes, MCompositionSurfaceUpdate* aReceiver)
       
   561 	{
       
   562 	TInt index = 1;
       
   563 	TInt numReceivers = 1;
       
   564 	TInt priority = 0;
       
   565 	
       
   566 	if(!aReceiver)
       
   567 		{
       
   568 		aReceiver = iUpdateReceiverEntryList[0]->iUpdateReceiver;
       
   569 		priority = iUpdateReceiverEntryList[0]->iPriority;
       
   570 		numReceivers = iUpdateReceiverEntryList.Count(); //get the number of all receivers present in the system
       
   571 		}
       
   572 	
       
   573 	for(;;)
       
   574 		{	
       
   575 		CUpdateReceiverNotification* receiverNotificationAvailable = NULL;
       
   576 		CUpdateReceiverNotification* receiverNotificationDisplayed = NULL;
       
   577 		CUpdateReceiverNotification* receiverNotificationDisplayedXTimes = NULL;
       
   578 		TUint32* timeStamp = NULL;
       
   579 	
       
   580 	//activate objects
       
   581 		if(iAvailable)
       
   582 			{
       
   583 			receiverNotificationAvailable = iAvailable->UpdateReceiverNotification();
       
   584 			if(!receiverNotificationAvailable)
       
   585 				{
       
   586 				iAvailable->iMsg.Complete(KErrNoMemory);
       
   587 				iAvailable->CheckForReuse();
       
   588 				iAvailable = NULL;
       
   589 				}
       
   590 			}
       
   591 
       
   592 		if(iDisplayed)
       
   593 			{
       
   594 			receiverNotificationDisplayed = iDisplayed->UpdateReceiverNotification(priority);
       
   595 			if(!receiverNotificationDisplayed)
       
   596 				{
       
   597 				iDisplayed->iMsg.Complete(KErrNoMemory);
       
   598 				iDisplayed->CheckForReuse();
       
   599 				iDisplayed = NULL;
       
   600 				}
       
   601 			}
       
   602 
       
   603 		if(iDisplayedXTimes)
       
   604 			{
       
   605 			receiverNotificationDisplayedXTimes = iDisplayedXTimes->UpdateReceiverNotification();
       
   606 			if(!receiverNotificationDisplayedXTimes)
       
   607 				{
       
   608 				iDisplayedXTimes->iMsg.Complete(KErrNoMemory);
       
   609 				iDisplayedXTimes->CheckForReuse();
       
   610 				iDisplayedXTimes = NULL;
       
   611 				}
       
   612 			}
       
   613 	
       
   614 		TRequestStatus *statusAvailable = NULL;
       
   615 		TRequestStatus *statusDisplayed = NULL;
       
   616 		TRequestStatus *statusDisplayedXTimes = NULL;
       
   617 	//activate all notifications
       
   618 		if(receiverNotificationAvailable)
       
   619 			{
       
   620 			statusAvailable = &(receiverNotificationAvailable->Status());
       
   621 			receiverNotificationAvailable->Activate();
       
   622 			}
       
   623 		if(receiverNotificationDisplayed)
       
   624 			{
       
   625 			statusDisplayed = &(receiverNotificationDisplayed->Status());
       
   626 			timeStamp = & receiverNotificationDisplayed -> iTimeStamp;
       
   627 			receiverNotificationDisplayed->Activate();
       
   628 			}
       
   629 		if(receiverNotificationDisplayedXTimes)
       
   630 			{
       
   631 			statusDisplayedXTimes = &(receiverNotificationDisplayedXTimes->Status());
       
   632 			receiverNotificationDisplayedXTimes->Activate();
       
   633 			}
       
   634 	//And finally, send request to the receiver   
       
   635 		aReceiver->ContentUpdated(aSurfaceId, aBuffer, aRegion,
       
   636 				statusAvailable, statusDisplayed, timeStamp, 
       
   637 				statusDisplayedXTimes, statusDisplayedXTimes ? aDisplayedXTimes : NULL);
       
   638 	
       
   639 		if(numReceivers > index)
       
   640 			{
       
   641 			priority = iUpdateReceiverEntryList[index]->iPriority;
       
   642 			aReceiver = iUpdateReceiverEntryList[index++]->iUpdateReceiver;
       
   643 			}
       
   644 		else
       
   645 			break;
       
   646 		}//for(;;)
       
   647 	
       
   648 	iAvailable = NULL;
       
   649 	iDisplayed = NULL;
       
   650 	iDisplayedXTimes = NULL;
       
   651 	}
       
   652 
       
   653 void CSurfaceUpdateSession::CancelAllUpdateNotifications()
       
   654 	{
       
   655 	//go through all notification objects and issue request complete for outstanding requests
       
   656 	TInt count = iUpdateReceiverNotificationBatches.Count();
       
   657 	
       
   658 	for(TInt index = 0; index < count; index++)
       
   659 		{
       
   660 		CUpdateReceiverNotificationBatch* notifier = iUpdateReceiverNotificationBatches[index];
       
   661 		if(!notifier->iMsg.IsNull())
       
   662 			{
       
   663 			notifier->iMsg.Complete(KErrCancel);
       
   664 			}
       
   665 		}
       
   666 
       
   667 	iAvailable = NULL;
       
   668 	iDisplayed = NULL;
       
   669 	iDisplayedXTimes = NULL;
       
   670 	}
       
   671 	
       
   672 //*********************
       
   673 CUpdateReceiverNotification::CUpdateReceiverNotification(CActive::TPriority aPriority, TInt aReceiverPriority, CUpdateReceiverNotificationBatch *aParentNotificationBatch) :
       
   674 	CActive(aPriority), iUpdateReceiverPriority(aReceiverPriority), iParentNotificationBatch(aParentNotificationBatch)
       
   675 	{
       
   676 	CActiveScheduler::Add(this);
       
   677 #ifdef TEST_SURFACE_UPDATE
       
   678 	iServer = iParentNotificationBatch->Server();
       
   679 #endif
       
   680 	}
       
   681 	
       
   682 CUpdateReceiverNotification::~CUpdateReceiverNotification()
       
   683 	{
       
   684 	//Content updates can not be cancelled. The user must ensure that the associated content update 
       
   685 	//is complete before deleting this object.
       
   686 	__ASSERT_ALWAYS(!IsActive(), CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity));
       
   687 	}
       
   688 	
       
   689 void CUpdateReceiverNotification::DoCancel()
       
   690 	{
       
   691 	//Content Updates can not be cancelled. Content Updates must be allowed to complete normally.
       
   692 	CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity);
       
   693 	}
       
   694 
       
   695 /**
       
   696 Is triggered every time the GCE signals on notification objects.
       
   697 Delegates processing to the parent notification batch.   
       
   698 */	
       
   699 void CUpdateReceiverNotification::RunL()
       
   700 	{
       
   701 	// CReceiverNotification object is deleted here once it is set to self destroying when run.
       
   702 	if (iSelfDestructWhenRun)
       
   703 		{
       
   704 #ifdef TEST_SURFACE_UPDATE
       
   705 		DecNumberPendingNotifications();
       
   706 #endif
       
   707 		delete this;
       
   708 		return;
       
   709 		}
       
   710 	iParentNotificationBatch->ProcessNotificationEvent(this);
       
   711 	}
       
   712 	
       
   713 TRequestStatus& CUpdateReceiverNotification::Status()
       
   714 	{
       
   715 	return iStatus;	
       
   716 	}
       
   717 
       
   718 void CUpdateReceiverNotification::Activate()
       
   719 	{
       
   720 	iStatus = KRequestPending;
       
   721 	SetActive();
       
   722 	}
       
   723 
       
   724 #ifdef TEST_SURFACE_UPDATE
       
   725 void CUpdateReceiverNotification::DecNumberPendingNotifications()
       
   726 	{
       
   727 	if (iServer)
       
   728 		{
       
   729 		iServer-> iNumberPendingNotification--;
       
   730 		}
       
   731 	}
       
   732 #endif
       
   733 
       
   734 /**
       
   735 The class maintains the notification objects of the same type and attributed to a 
       
   736 particular submit update command.   
       
   737  */
       
   738 CUpdateReceiverNotificationBatch::CUpdateReceiverNotificationBatch(CSurfaceUpdateSession *aSession, TInt aNumReceivers) :
       
   739 	iSession(aSession), iNumUpdateReceivers(aNumReceivers)
       
   740 	{
       
   741 	CheckForReuse();
       
   742 	}
       
   743 
       
   744 CUpdateReceiverNotificationBatch::~CUpdateReceiverNotificationBatch()
       
   745 	{
       
   746 	TInt count = iUpdateReceiverNotifications.Count();
       
   747 	for(TInt index = 0; index < count; index++)
       
   748 		{
       
   749 		CUpdateReceiverNotification* notifier = iUpdateReceiverNotifications[index];
       
   750 		// CReceiverNotification active object cannot be destroyed if it is active and waiting for
       
   751 		// receiver to complete the notification. It deletes itself inside its RunL function when the
       
   752 		// notification is completed.
       
   753 		if (notifier->IsActive())
       
   754 			{
       
   755 			notifier->iSelfDestructWhenRun = ETrue;
       
   756 #ifdef TEST_SURFACE_UPDATE
       
   757 			IncNumberPendingNotifications();
       
   758 #endif
       
   759 			}
       
   760 		else
       
   761 			{
       
   762 			delete notifier;
       
   763 			}
       
   764 		}
       
   765 	iUpdateReceiverNotifications.Close();
       
   766 	}
       
   767 
       
   768 /**
       
   769 The function processes signal from composition receiver. 
       
   770 It completes outstanding messages on the client and set timestamp parameters if appropriate.
       
   771 Note that this object will not be removed from the 
       
   772 array (CSurfaceUpdateSession:: iReceiverNotifications) and can be reused later.
       
   773 */
       
   774 void CUpdateReceiverNotificationBatch::ProcessNotificationEvent(CUpdateReceiverNotification* aReceiverNotification)
       
   775 	{
       
   776 	TInt index = iUpdateReceiverNotifications.Find(aReceiverNotification);
       
   777 	__ASSERT_DEBUG(index != KErrNotFound, CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity));
       
   778 	iUpdateReceiverNotifications.Remove(index);
       
   779 	if(iMsg.IsNull())
       
   780 		{
       
   781 		CheckForReuse();
       
   782 		delete aReceiverNotification;
       
   783 		return; // the message has already been completed, possibly, because of request cancelation
       
   784 		}
       
   785 	TBool oldCompleteWithSuccess = iCompleteWithSuccess;
       
   786 	TBool newCompleteWithSuccess = EFalse;
       
   787 	if(aReceiverNotification->iStatus.Int() == KErrNone)
       
   788 		{
       
   789 		iCompleteWithSuccess = ETrue;
       
   790 		newCompleteWithSuccess = ETrue;
       
   791 		}
       
   792 	
       
   793 	switch(iType)
       
   794 		{
       
   795 	case EUpdateSrvAvailable:
       
   796 		if(iUpdateReceiverNotifications.Count() == 0)
       
   797 			{
       
   798 			TInt res = iCompleteWithSuccess ? KErrNone : aReceiverNotification->iStatus.Int();
       
   799 			iMsg.Complete(res);
       
   800 			}
       
   801 		break;
       
   802 	case EUpdateSrvDisplayedXTimes:
       
   803 		if((newCompleteWithSuccess && (index == 0)) || (iUpdateReceiverNotifications.Count() == 0))
       
   804 			{
       
   805 			TInt res = iCompleteWithSuccess ? KErrNone : aReceiverNotification->iStatus.Int();
       
   806 			iMsg.Complete(res);
       
   807 			}
       
   808 		break;
       
   809 	case EUpdateSrvDisplayed:
       
   810 		if(newCompleteWithSuccess && 
       
   811 				((iHighestPriority < aReceiverNotification->iUpdateReceiverPriority) || !oldCompleteWithSuccess))
       
   812 			{
       
   813 			iHighestPriority = aReceiverNotification->iUpdateReceiverPriority;
       
   814 			iTimeStamp = aReceiverNotification->iTimeStamp;
       
   815 			}
       
   816 		if((newCompleteWithSuccess && (index == 0)) || (iUpdateReceiverNotifications.Count() == 0))
       
   817 			{
       
   818 			TPckgBuf<TUint32> p(iTimeStamp);
       
   819 			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.
       
   820 			if(res == KErrNone)
       
   821 				{
       
   822 				res = iCompleteWithSuccess ? KErrNone : aReceiverNotification->iStatus.Int();
       
   823 				}
       
   824 			iMsg.Complete(res);
       
   825 			}
       
   826 		break;
       
   827 	default:	
       
   828 		break;
       
   829 		}
       
   830 	
       
   831 	delete aReceiverNotification;
       
   832 	CheckForReuse();
       
   833 	}
       
   834 
       
   835 /**
       
   836  The method allocates a notification object and inserts it into the list
       
   837 */ 
       
   838 CUpdateReceiverNotification* CUpdateReceiverNotificationBatch::UpdateReceiverNotification(TInt aReceiverPriority)
       
   839 	{
       
   840 	__ASSERT_DEBUG(iNumUpdateReceivers > iUpdateReceiverNotifications.Count(), CSurfaceUpdateServer::PanicServer(EUpdateServPanicDataIntegrity));
       
   841 	
       
   842 	CUpdateReceiverNotification* receiverNotification = new CUpdateReceiverNotification(CActive::EPriorityStandard, aReceiverPriority, this);
       
   843 	if(receiverNotification)
       
   844 		{
       
   845 		TInt res = iUpdateReceiverNotifications.Append(receiverNotification);
       
   846 		if(res != KErrNone)
       
   847 			{
       
   848 			delete receiverNotification;
       
   849 			receiverNotification = NULL;
       
   850 			}
       
   851 		}
       
   852 	
       
   853 	return receiverNotification;
       
   854 	}
       
   855 
       
   856 /**
       
   857 If notification list is empty mark the batch for further use
       
   858 */
       
   859 void CUpdateReceiverNotificationBatch::CheckForReuse()
       
   860 	{
       
   861 	if(iUpdateReceiverNotifications.Count() == 0)
       
   862 		{
       
   863 		iType = EUpdateSrvReusable;
       
   864 		iCompleteWithSuccess = EFalse;
       
   865 		iHighestPriority = 0;
       
   866 		iTimeStamp = 0;
       
   867 		}
       
   868 	}
       
   869 
       
   870 #ifdef TEST_SURFACE_UPDATE
       
   871 CSurfaceUpdateServer* CUpdateReceiverNotificationBatch::Server()
       
   872 	{
       
   873 	return (CSurfaceUpdateServer*)(iSession->Server());
       
   874 	}
       
   875 
       
   876 void CUpdateReceiverNotificationBatch::IncNumberPendingNotifications()
       
   877 	{
       
   878 	CServer2* server = static_cast<CServer2*> (Server());
       
   879 	if(server)
       
   880 		{
       
   881 		(static_cast<CSurfaceUpdateServer*> (server))-> iNumberPendingNotification++;
       
   882 		}
       
   883 	}
       
   884 #endif
       
   885 
       
   886 /**
       
   887 
       
   888 The class will be used by composition receiver
       
   889 */
       
   890 CSurfaceUpdateServerProvider* CSurfaceUpdateServerProvider::NewL(CActive::TPriority aPriority, CSurfaceUpdateServer* aServer)
       
   891 	{
       
   892 	CSurfaceUpdateServerProvider* serverProvider = new (ELeave) CSurfaceUpdateServerProvider(aPriority, aServer);
       
   893 	CleanupStack::PushL(serverProvider);
       
   894 	serverProvider->ConstructL();
       
   895 	CleanupStack::Pop();
       
   896 	return serverProvider;
       
   897 	}
       
   898 
       
   899 CSurfaceUpdateServerProvider::CSurfaceUpdateServerProvider(CActive::TPriority aPriority, CSurfaceUpdateServer* aServer) :
       
   900 								CActive(aPriority), iServer(aServer)
       
   901 	{
       
   902 	RThread thread;
       
   903 	iThreadId = thread.Id();
       
   904 	}
       
   905 	
       
   906 CSurfaceUpdateServerProvider::~CSurfaceUpdateServerProvider()
       
   907 	{
       
   908 	iSemaphore.Close();
       
   909 	Cancel();
       
   910 	}
       
   911 /**
       
   912 Create provider and add it to the active scheduler
       
   913 */
       
   914 void CSurfaceUpdateServerProvider::ConstructL()
       
   915 	{
       
   916 	User::LeaveIfError(iSemaphore.CreateLocal(0));
       
   917 	CActiveScheduler::Add(this);
       
   918 	iStatus = KRequestPending;
       
   919 	SetActive();
       
   920 	}
       
   921 
       
   922 /**  
       
   923  Assign composition receiver instance to particular screen.
       
   924  The following calls of this function for the same screen will override the previous.
       
   925  @see MSurfaceUpdateServerProvider::::Register
       
   926  */	
       
   927 EXPORT_C TInt CSurfaceUpdateServerProvider::Register(TInt aScreen, CBase* aReceiver, TInt aPriority)
       
   928 	{
       
   929 	RThread thread;
       
   930 	TInt res = thread.Open(iThreadId);
       
   931 	if(res == KErrNone)
       
   932 		{
       
   933 		iScreen = aScreen;
       
   934 		iUpdateReceiver = aReceiver;
       
   935 		iPriority = aPriority;
       
   936 
       
   937 		TRequestStatus* status = &iStatus;
       
   938 		thread.RequestComplete(status, EUpdateServEventRegister);
       
   939 		thread.Close();
       
   940 		iSemaphore.Wait();
       
   941 		res = iRegisterErr;
       
   942 		}
       
   943 	return res;
       
   944 	}
       
   945 
       
   946 /**
       
   947 Terminate the SUS and release all memory associated with it. 
       
   948 The function was introduced for debugging purpose only and is not considered 
       
   949 as part of public API.
       
   950 */
       
   951 EXPORT_C void CSurfaceUpdateServerProvider::Terminate()
       
   952 	{
       
   953 #ifdef TEST_SURFACE_UPDATE
       
   954 	RThread thread;
       
   955 
       
   956 	if(thread.Open(iThreadId) == KErrNone)
       
   957 		{
       
   958 		if (iServer)
       
   959 			{
       
   960 			while((static_cast<CSurfaceUpdateServer*> (iServer))-> iNumberPendingNotification)
       
   961 				User::After(TTimeIntervalMicroSeconds32(1000));
       
   962 			}
       
   963 		TRequestStatus* status = &iStatus;
       
   964 		thread.RequestComplete(status, EUpdateServEventTerminate);
       
   965 		
       
   966 		//wait until the thread dies
       
   967 		TRequestStatus status1;
       
   968 		thread.Logon(status1);
       
   969 		User::WaitForRequest(status1);
       
   970 		thread.Close();
       
   971 		
       
   972 		Dll::SetTls(NULL);
       
   973 		}
       
   974 #endif
       
   975 	}
       
   976 
       
   977 /**
       
   978 The function processes signals which come from composition receiver thread.
       
   979 */	
       
   980 void CSurfaceUpdateServerProvider::RunL()
       
   981 	{
       
   982 	switch(iStatus.Int())
       
   983 		{
       
   984 	case EUpdateServEventTerminate:
       
   985 		CActiveScheduler::Stop();
       
   986 		return; 
       
   987 	case EUpdateServEventRegister:
       
   988 		iRegisterErr = iServer->Register(iScreen, iUpdateReceiver, iPriority);
       
   989 		iUpdateReceiver = NULL;
       
   990 		iStatus = KRequestPending;
       
   991 		SetActive();
       
   992 		iSemaphore.Signal();
       
   993 		break;
       
   994 	default :
       
   995 		CSurfaceUpdateServer::PanicServer(EUpdateServPanicBadRequest); 
       
   996 		iStatus = KRequestPending;
       
   997 		SetActive();
       
   998 		break;	
       
   999 		}
       
  1000 	}
       
  1001 	
       
  1002 void CSurfaceUpdateServerProvider::DoCancel()
       
  1003 	{
       
  1004 	}
       
  1005 
       
  1006 /**
       
  1007 Spawn a thread within WSERV process. This will lead to starting the surface update server in it 
       
  1008 @publishedPartner
       
  1009 @prototype Intended to be used by Surface Update control flow
       
  1010 	
       
  1011 @param aUpdateProvider - [out] reference pointer to surface update server provider, 
       
  1012 		which will be set when the server is started. The variable will be used to registry 
       
  1013 		composition receiver instances. The caller doesn't acquire the ownership of this instance,
       
  1014 		thus mustn't delete it. The pointer will be valid until server is operating, 
       
  1015 		i.e. system is up.
       
  1016 
       
  1017 @panic KErrAccessDenied	If is called from process other than WSERV.	
       
  1018 @return KErrNone if an operation is successful, any other system error codes otherwise
       
  1019 */
       
  1020 EXPORT_C TInt StartSurfaceUpdateServer(MSurfaceUpdateServerProvider*& aSurfaceUpdateServerProvider)
       
  1021 	{
       
  1022 #ifndef TEST_SURFACE_UPDATE
       
  1023 	RProcess process;
       
  1024 	TUidType uidType = process.Type();
       
  1025 	const TInt32 KWservUid = 268450592;
       
  1026 	const TUid& uid1 = uidType[2];
       
  1027 
       
  1028 	if(uid1.iUid != KWservUid) //only wserv process can start the server
       
  1029 		{// some malicious client tries to launch the server
       
  1030 		process.Panic(_L("Access denied"), KErrAccessDenied);
       
  1031 		return KErrAccessDenied;
       
  1032 		}	  
       
  1033 	TPtrC serverName(KSurfaceUpdateServerName);
       
  1034 #else
       
  1035 	TPtrC serverName(KTestSurfaceUpdateServerName);
       
  1036 #endif
       
  1037 	TAny *provider = Dll::Tls();
       
  1038 	if(provider)
       
  1039 		{
       
  1040 		aSurfaceUpdateServerProvider = static_cast <MSurfaceUpdateServerProvider*> (provider);
       
  1041 		return KErrNone;
       
  1042 		}
       
  1043 	TFullName   name;
       
  1044 	RThread serverThread;
       
  1045 	TInt res = KErrAlreadyExists;
       
  1046 	
       
  1047 	TFindServer findSurfaceUpdateServer(serverName);
       
  1048 	  // Need to check that the server exists.
       
  1049 	if (findSurfaceUpdateServer.Next(name) !=KErrNone)
       
  1050 		{
       
  1051 		TTime tm;
       
  1052 		TBuf<32> buf;
       
  1053 		tm.UniversalTime();
       
  1054 		TRAP(res, tm.FormatL(buf, _L("_%H%T%S%C")));
       
  1055 		if(res != KErrNone)	
       
  1056 			{
       
  1057 			return res;
       
  1058 			}
       
  1059 		TBuf<128> threadName(serverName);
       
  1060 		threadName.Append(buf); //guarantee uniqueness  of the thread name
       
  1061 		  // Create the thread for the server.
       
  1062 		res = serverThread.Create(threadName,
       
  1063 			CSurfaceUpdateServer::ThreadFunction,
       
  1064 			KDefaultStackSize,
       
  1065 			KDefaultHeapSize,
       
  1066 			KDefaultHeapSize,
       
  1067 			(TAny*) &aSurfaceUpdateServerProvider
       
  1068 			);
       
  1069           // The thread has been created OK so get it started - however
       
  1070           // we need to make sure that it has started before we continue.
       
  1071 		if (res==KErrNone)
       
  1072 			{
       
  1073 			TRequestStatus rendezvousStatus;
       
  1074 			TThreadPriority priority = RThread().Priority();    
       
  1075 			serverThread.SetPriority(priority); // The same as the priority of the creating thread
       
  1076 			serverThread.Rendezvous(rendezvousStatus);
       
  1077 			serverThread.Resume();
       
  1078 			User::WaitForRequest(rendezvousStatus);
       
  1079 			res = rendezvousStatus.Int();
       
  1080 			Dll::SetTls(aSurfaceUpdateServerProvider);
       
  1081 			}
       
  1082     // The thread has not been created - clearly there's been a problem.
       
  1083 		else
       
  1084 			{
       
  1085 			serverThread.Close();
       
  1086 			}
       
  1087 		}
       
  1088 		return res;
       
  1089 	}