kernel/eka/ewsrv/ws_main.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 137 8aab599e3476
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// e32\ewsrv\ws_main.cpp
// 
//

#include "ws_std.h"
#include <e32hal.h>
#include <hal.h>
#include <e32math.h>
#include <domainmanager.h>
#ifdef __WINS__
#include <e32wins.h>
#endif

GLREF_D CKeyTranslator *KeyTranslator;
GLREF_D CKeyRepeat* KeyRepeat;

// password notifier support functions
LOCAL_C void RenderPassword(RConsole *aCon, TInt aPWLeft, const TDesC& aPW);

_LIT(KShellProcessName, "ESHELL");
_LIT(KShellCommandLine, "/p");

//
// class CKeyRepeat
//

CKeyRepeat::CKeyRepeat(TInt aPriority) : CTimer(aPriority)
//
// Constructor. Set default repeat delay and rate
//
    {
    iDelay=EDefaultKeyRepeatDelay;
    iRate=EDefaultKeyRepeatRate;
    }

void CKeyRepeat::ConstructL()
    {

    CTimer::ConstructL();
    CActiveScheduler::Add(this);
    }

void CKeyRepeat::RunL()
//
// Send a repeat keypress to the window
//
    {

    After(iRate);
	CWsWindow::KeyPress(iKeyData);
    }

void CKeyRepeat::Request(TKeyData& aKeyData)
//
// Request a repeat event
//
    {

    iKeyData=aKeyData;
	Cancel();
    After(iDelay);
    }

void CKeyRepeat::SetRepeatTime(TInt aDelay,TInt aRate)
    {

    iDelay=aDelay;
    iRate=aRate;
    }

void CKeyRepeat::RepeatTime(TInt& aDelay,TInt& aRate)
    {

    aDelay=iDelay;
    aRate=iRate;
    }

//
// class CWsSession
//

CWsSession::CWsSession()
	{
	iTestFast = (UserSvr::DebugMask(2)&0x00000002) ? 1 : 0;
	}

CWsSession::~CWsSession()
//
// Destructor
//
	{

	delete iWindow;
	}

//
// class CWsServer
//

void CWsServer::New()
//
// Create a new CWsServer.
//
	{

	CWsServer *pS=new CWsServer(EPriority);
	__ASSERT_ALWAYS(pS!=NULL,Fault(ECreateServer));
	pS->SetPinClientDescriptors(EFalse); // don't pin because client interface can't cope with errors if pin fails under real or simulated OOM
	TInt r=pS->Start(KE32WindowServer);
	__ASSERT_ALWAYS(r==KErrNone,Fault(EStartServer));
	RProcess::Rendezvous(KErrNone);

	}

CWsServer::CWsServer(TInt aPriority)
//
// Constructor.
//
	: CServer2(aPriority)
	{
	}

CSession2* CWsServer::NewSessionL(const TVersion& aVersion,const RMessage2&) const
//
// Create a new client for this server.
//
	{

	TVersion v(KW32MajorVersionNumber,KW32MinorVersionNumber,KE32BuildVersionNumber);
	TBool r=User::QueryVersionSupported(v,aVersion);
	if (!r)
		User::Leave(KErrNotSupported);
	return new(ELeave) CWsSession;
	}

void CWsSession::ServiceL(const RMessage2& aMessage)
//									    
// Handle messages for this session.
//
	{

	iCurMsg = aMessage;
	CWsWindow::WaitOnService();
	CWsWindow* pW=iWindow;
	TInt r=EPrematureOperation;
	TBool delayCompletion=EFalse;
	switch (aMessage.Function())
		{
	case EConsoleCreate:
		{
		if (pW)
			{
			delete pW;
			iWindow=NULL;
			}
		pW=new CWsWindow;
		if (!pW)
			{ 
			r=EWindowOutOfMemory;
			break;
			}
		iWindow=pW;
		pW->iAllowResize=ETrue;
		pW->iIsVisible=ETrue;
		pW->iOnTop=EFalse;
		pW->SetCursorHeight(50);
		pW->iKQueue.SetOffset(_FOFF(SWsKey,iLink));
		break;
		}
	case EConsoleSet:
		{
		if (!pW)
			{
			pW=new(ELeave) CWsWindow;
			iWindow=pW;
			pW->iAllowResize=ETrue;
			pW->iIsVisible=ETrue;
			pW->iOnTop=EFalse;
			pW->SetCursorHeight(50);
			pW->iKQueue.SetOffset(_FOFF(SWsKey,iLink));
			}
		pW->iAllowSlide=ETrue;
		TFileName name;
		iCurMsg.ReadL(0, name);
		pW->iTitle=name;
		TPckgBuf<TSize> size;
		iCurMsg.ReadL(1, size);
		TRAP(r,pW->CreateL(size()));
		if (r != KErrNone)
			{
			delete pW;
			iWindow=NULL;			
			}
		break;
		}
	case EConsoleClearScreen:
		{
		if (pW)
			{
			pW->Clear();
			r=KErrNone;
			}
		break;
		}
	case EConsoleClearToEndOfLine:
		{
		if (pW)
			{
			pW->ClearToEndOfLine();
			r=KErrNone;
			}
		break;
		}
	case EConsoleSetTitle:
		{
		if (pW)
			{
			TFileName name;
			iCurMsg.ReadL(0, name);
			pW->SetTitle(name);
			r=KErrNone;
			}
		break;
		}
	case EConsoleSetSize:
		{
		if (pW)
			{
			TPckgBuf<TSize> size;
			iCurMsg.ReadL(0, size);
//			pW->SetSize(size());
//			r=KErrNone;
			r=KErrNotSupported;
			}
		break;
		}
	case EConsoleSetWindowPosAbs:
		{
		if (pW)
			{
			TPckgBuf<TPoint> point;
			iCurMsg.ReadL(0, point);
			pW->SetWindowPosAbs(point());
			r=KErrNone;
			}
		break;
		}
	case EConsoleSetCursorHeight:
		{
		if (pW)
			{
			pW->SetCursorHeight(aMessage.Int0());
			r=KErrNone;
			}
		break;
		}
	case EConsoleSetCursorPosAbs:
		{
		if (pW)
			{
			TPckgBuf<TPoint> point;
			iCurMsg.ReadL(0, point);
			pW->SetCursorPosAbs(point());
			r=KErrNone;
			}
		break;
		}
	case EConsoleSetCursorPosRel:
		{
		if (pW)
			{
			TPckgBuf<TPoint> point;
			iCurMsg.ReadL(0, point);
			pW->SetCursorPosRel(point());
			r=KErrNone;
			}
		break;
		}
	case EConsoleCursorPos:
		{
		if (pW)
			{
			TPckgBuf<TPoint> point;
			point()=pW->CursorPosition();
			aMessage.WriteL(0,point);
			r=KErrNone;
			}
		break;
		}
	case EConsoleSize:
		{
		if (pW)
			{
			TPckgBuf<TSize> size;
			size()=pW->Size();
			aMessage.WriteL(0,size);
			r=KErrNone;
			}
		break;
		}
	case EConsoleScreenSize:
		{
		if (pW)
			{
			TPckgBuf<TSize> size;
			size()=CWsWindow::ScreenSize;
			aMessage.WriteL(0,size);
			r=KErrNone;
			}
		break;
		}
	case EConsoleControl:
		{
		if (pW)
			{
			TBool indicator=ETrue;
			TInt offset=0;
			TBuf<0x100> b;
			do
				{
				iCurMsg.ReadL(0,b,offset);
				for (const TText* pB=b.Ptr();pB<b.Ptr()+b.Length();pB++)
					{
					switch(*pB)
						{
					case '+':

						indicator=ETrue;
						break;
					case '-':
						indicator=EFalse;
						break;
					case 'S':
						pW->ControlScrollBars(indicator);
						break;
					case 'W':
						pW->ControlWrapLock(indicator);
						break;
                    case 'P':
                        pW->ControlPointerEvents(indicator);
                        break;
					case 'L':
						pW->ControlScrollLock(indicator);
						break;
					case 'V':
						pW->ControlVisibility(indicator);
						break;
					case 'C':
						pW->ControlCursorRequired(indicator);
						break;
					case 'M':
						pW->ControlMaximised(indicator);
						break;
					case 'N':
						pW->ControlNewLineMode(indicator);
						break;
                    case 'O':
						pW->ControlOnTop(indicator);
						break;
					case 'I':
                        pW->ControlInformAllMouse(indicator);
                        break;
                    case 'R':
                        pW->ControlRawEventMode(indicator);
                        break;
					case 'A':
						pW->ControlAllowResize(indicator);
						}
					}
				offset+=b.Length();
				}
			while (b.Length()==b.MaxLength());
			r=KErrNone;
			}
		break;
		}
	case EConsoleWrite:
		{
		if (pW)
			{
			switch(iTestFast)
				{
			case 0:
				{
				TInt offset=0;
				TBuf<0x100> b;
				do
					{
					iCurMsg.ReadL(0,b,offset);
					pW->Write(b);
					offset+=b.Length();
					}
				while (b.Length()==b.MaxLength());
				pW->WriteDone();
				}
				r=KErrNone;
				break;

			case 1:
				{
				pW->Write(_L("Output suppressed because TESTFAST mode is set..."));
				pW->WriteDone();
				++iTestFast;
				}
				r=KErrNone;
				break;

			default:
				r=KErrNone;
				break;
				}
			}
		break;
		}
	case EConsoleRead:
		{
		if (pW)
			{
			if (pW->EnqueReadRequest(aMessage))
				{
				delayCompletion=ETrue;
				r=KErrNone;
				}
			else
				r=EDoubleReadRequest;
			}
		break;
		}
	case EConsoleReadCancel:
		{
		if (pW)
			{
			pW->DequeReadRequest();
			r=KErrNone;
			}
		break;
		}
	case EConsoleDestroy:
		{
		if (pW)
			{
			delete pW;
			iWindow=NULL;
			r=KErrNone;
			}
		break;
		}
	case EConsoleSetMode:
		{
		r=CWsWindow::SetMode((TVideoMode)aMessage.Int0());		
		break;
		}
	case EConsoleSetPaletteEntry:
		{
		CWsWindow::ScreenDriver->SetPaletteEntry((TColorIndex)aMessage.Int0(),(TUint8)aMessage.Int1(),(TUint8)aMessage.Int2(),(TUint8)aMessage.Int3());
		pW->Redraw();
		r=KErrNone;
		break;
		}
	case EConsoleGetPaletteEntry:
		{
		TUint8 r,g,b;
		TPckgBuf<TUint8> red,green,blue;
		CWsWindow::ScreenDriver->GetPaletteEntry((TColorIndex)aMessage.Int0(),r,g,b);
		red()=r; green()=g; blue()=b;
		aMessage.WriteL(1,red);
		aMessage.WriteL(2,green);
		aMessage.WriteL(3,blue);
		}
		r=KErrNone;
		break;
	case EConsoleSetTextColors:
		{
		if(pW)
			{
			pW->iFgColor=(TColorIndex)aMessage.Int0();
			pW->iBgColor=(TColorIndex)aMessage.Int1();
			}		
		r=KErrNone;
		break;
		}
	case EConsoleSetUIColors:
		{
		CWsWindow::WindowBgColor=(TColorIndex)aMessage.Int0();
		CWsWindow::BorderColor=(TColorIndex)aMessage.Int1();
		CWsWindow::ScreenColor=(TColorIndex)aMessage.Int2();
		CWsWindow::ChangeUIColors();
		r=KErrNone;
		break;
		}
	case EConsoleSetTextAttribute:
		{
		if(pW)
			pW->SetTextAttribute((TTextAttribute)aMessage.Int0());		
		r=KErrNone;
		break;
		}
	default:
		r=KErrNotSupported;
		}
	if (!delayCompletion)
		aMessage.Complete(r);
	CWsWindow::SignalService();
	}

void CWsSession::ServiceError(const RMessage2& aMessage,TInt aError)
	{
	if (!aMessage.IsNull())
		{
		if (aError>0)
			{
			aMessage.Panic(_L("WServ panic"),aError);
			}
		else
			{
			aMessage.Complete(aError);
			}
		}
	}

CWsServer::~CWsServer()
//
// Destructor
//
	{
	}

//
// class CEvent
//

void CEvent::New()
//
// Create the CEvent active object.
//
	{

	CEvent *pE=new CEvent(EPriority);
	__ASSERT_ALWAYS(pE!=NULL,Fault(ECreateEvent));
	pE->CaptureKeys=new CCaptureKeys();
	__ASSERT_ALWAYS(pE->CaptureKeys!=NULL,Fault(ECreateEvent));

	pE->CaptureKeys->Construct();

	CActiveScheduler::Add(pE);
	UserSvr::CaptureEventHook();
	pE->Request();
	}

CEvent::~CEvent()
//
// Destroy the CEvent active object
//
	{

	Cancel();
	}

#pragma warning( disable : 4705 )	// statement has no effect
CEvent::CEvent(TInt aPriority)
//
// Constructor
//
	: CActive(aPriority)
	{
	}
#pragma warning( default : 4705 )

void CEvent::Request()
//
// Issue a request for the next event.
//
	{

	UserSvr::RequestEvent(iEvent,iStatus);
	SetActive();
	}

void CEvent::DoCancel()
//
// Cancel a pending event.
//
	{

	UserSvr::RequestEventCancel();
	}

void CEvent::RunL()
//
// Event has completed.
//
	{

    if (CWsWindow::RawEventMode())
        {
        KeyRepeat->Cancel();
        CWsWindow::QueueRawEvent(iEvent.Event());
        Request();
        return;
        }
	switch(iEvent.Event().Type())
		{
	case TRawEvent::ERedraw:
		CWsWindow::Redraw();
		break;
	case TRawEvent::EButton1Down:
        if(!CWsWindow::MouseIsCaptured)
            {
      		CWsWindow::MouseMove(iEvent.Event().Pos());
  	    	CWsWindow::MouseLeftButton();
            }
        else
            CWsWindow::InformTopMouse(iEvent.Event().Pos());
		break;
	case TRawEvent::EButton1Up:
		if(!CWsWindow::MouseIsCaptured)
			{
			CWsWindow::MouseMove(iEvent.Event().Pos());
			CWsWindow::MouseLeftButtonUp();
			}
		break;
	case TRawEvent::EButton2Down:
		break;
	case TRawEvent::EButton2Up:
		break;
	case TRawEvent::EButton3Down:
		break;
	case TRawEvent::EButton3Up:
		break;
	case TRawEvent::EPointerMove:
		CWsWindow::MouseMove(iEvent.Event().Pos());
		break;
	case TRawEvent::EInactive:
        KeyRepeat->Cancel();
        break;
    case TRawEvent::EActive:
        break;
    case TRawEvent::EUpdateModifiers:
        KeyTranslator->UpdateModifiers(iEvent.Event().Modifiers());
        break;
	case TRawEvent::EKeyDown:
		{
		TKeyData keyData;
		if (KeyTranslator->TranslateKey(iEvent.Event().ScanCode(), EFalse,*CaptureKeys,keyData))
			CWsWindow::KeyPress(keyData);
		if (keyData.iModifiers&EModifierAutorepeatable)
			KeyRepeat->Request(keyData);
		break;
		}
	case TRawEvent::EKeyUp:
		{
		TKeyData keyData;
		KeyRepeat->Cancel();
		if (KeyTranslator->TranslateKey(iEvent.Event().ScanCode(), ETrue,*CaptureKeys,keyData))
			CWsWindow::KeyPress(keyData);
		break;
		}
	case TRawEvent::ESwitchOn:
	case TRawEvent::ECaseOpen:
		HAL::Set(HAL::EDisplayState, 1);
			{
			RDmDomainManager mgr; 
			TInt r = mgr.Connect();
			if (r != KErrNone)
				User::Panic(_L("EWSRV SwitchOn0"), r);
			TRequestStatus status;
			mgr.RequestDomainTransition(KDmIdUiApps, EPwActive, status);
			User::WaitForRequest(status);
			if (status.Int() != KErrNone)
				User::Panic(_L("EWSRV SwitchOn1"), status.Int());
			mgr.Close();
			}
		break;
	case TRawEvent::EPointerSwitchOn:
#if defined(_DEBUG)
    	User::Beep(440,250000);
#endif
		break;
	case TRawEvent::ESwitchOff:
			{
			RDmDomainManager mgr; 
			TInt r = mgr.Connect();
			if (r != KErrNone)
				User::Panic(_L("EWSRV SwitchOff0"), r);
			TRequestStatus status;
			mgr.RequestSystemTransition(EPwStandby, status);
			User::WaitForRequest(status);
			if (status.Int() != KErrNone)
				User::Panic(_L("EWSRV SwitchOff1"), status.Int());
			mgr.Close();
			}
		break;
	case TRawEvent::ECaseClose:
			{
			RDmDomainManager mgr; 
			TInt r = mgr.Connect();
			if (r != KErrNone)
				User::Panic(_L("EWSRV CaseClosed"), r);
			TRequestStatus status;
			mgr.RequestDomainTransition(KDmIdUiApps, EPwStandby, status);
			User::WaitForRequest(status);
			if (status.Int() != KErrNone)
				User::Panic(_L("EWSRV CaseClosed1"), status.Int());
			mgr.Close();
			}
		HAL::Set(HAL::EDisplayState, 0);
		break;
	case TRawEvent::ENone:
		break;
	default:
		break;
		}                                       
	Request();
	}

LOCAL_C void RenderPassword(RConsole *aCon, TInt aPWLeft, const TDesC& aPW)
// pre:		aCon points to a console being used to read a password.
//			aPWLeft is the column number from which the left brace should be drawn.
//			aPasswd is a valid password.
// post:	the password is rendered onto the console and followed by a '#' and
//			padding spaces.  Everything is enclosed in a pair of square brackets.
	{
	aCon->SetCursorPosAbs(TPoint(aPWLeft, 3));
	aCon->Write(_L("["));
	aCon->Write(aPW);
	aCon->Write(_L("#"));

	TInt i;
	for (i = 0; i < KMaxMediaPassword - aPW.Length(); i++)
		{
		aCon->Write(_L(" "));
		}

	aCon->Write(_L("]"));
	}


void CNotifierSession::RunPasswordWindowL(const RMessage2 &aMsg)
//
// Eight unicode chars are mapped to (up to) sixteen bytes.  Remainder of array is
// zeroed.  Message is completed in CNotifierSession::ServiceL().
// 
	{
	// local copies of dialog data, 5 * (8 + 32 * 2) bytes
	TBuf<0x20> line1, line2, unlockBtn, storeBtn, cancelBtn;
	line1.Copy(_L("Password notifier"));
	line2.Copy(_L("Enter password"));
	unlockBtn.Copy(_L("Unlock"));
	storeBtn.Copy(_L("Store"));
	cancelBtn.Copy(_L("Cancel"));

	TPckgBuf<TMediaPswdReplyNotifyInfoV1> reply;

	// Format the console window.

	const TInt KPasswordBarLen(1 + KMaxMediaPassword + 1 + 1);
	const TInt KButtonBarLen(
				1 + unlockBtn.Length() + 1
		+ 1 +	1 + cancelBtn.Length() + 1
		+ 1 +	1 + storeBtn.Length() + 1);

	// Work out width of window.
	// (Buttons are enclosed by angle brackets and separted by a space.)
	// (Password is followed by '#' and delimited by square brackets.)
	// If KButtonBarLen is at least as long as any other line then it will write
	// to the bottom right corner and cause the console to scroll.  To counter
	// this, an extra padding character is added if necessary.

	TInt width;
	width = Max(line1.Length(), line2.Length());
	width = Max(width, KPasswordBarLen);
	width = KButtonBarLen >= width ? KButtonBarLen + 1: width;

	// Create the window and render its contents.

	RConsole con;
	con.Create();
	con.Control(_L("-Visible"));
	TInt r = con.Init(_L(""), TSize(width, 2 + 1 + 1 + 1 + 1));
	if (KErrNone != r)
		{
		PanicClient(aMsg, ENotifierPanicPasswordWindow);
		User::Leave(KErrGeneral);
		}
	con.Control(_L("+Max -Cursor -AllowResize +OnTop"));

	con.SetCursorPosAbs(TPoint(0, 0));
	con.Write(line1);
	con.SetCursorPosAbs(TPoint(0, 1));
	con.Write(line2);
	const TInt KPasswordLeft((width - KPasswordBarLen) / 2);
	con.SetCursorPosAbs(TPoint(KPasswordLeft, 3));
	con.Write(_L("[#                ]"));
	con.SetCursorPosAbs(TPoint((width - KButtonBarLen) / 2, 5));
	con.Write(_L("<"));
	con.Write(unlockBtn);
	con.Write(_L("> <"));
	con.Write(storeBtn);
	con.Write(_L("> <"));
	con.Write(cancelBtn);
	con.Write(_L(">"));

	// Allow the user to edit the password until they either press enter or escape.

	TBool done(EFalse);
	TBuf<KMaxMediaPassword> pw;
	pw.Zero();
	TMediaPswdNotifyExitMode em(EMPEMUnlock);	// avoid VC warning C4701 (used w/o init).

	const TInt sendInfoLen = User::LeaveIfError(aMsg.GetDesLength(1));

	if (sendInfoLen == sizeof(TMediaPswdSendNotifyInfoV1Debug))
		{
		// debug mode - wait for specified period and close notifier

		TPckgBuf<TMediaPswdSendNotifyInfoV1Debug> sendDbg;
		aMsg.ReadL(1, sendDbg);
		if (sendDbg().iSleepPeriod >= 0)
			User::After(sendDbg().iSleepPeriod);
		else
			{
			TTime now;
			now.HomeTime();
			TInt64 seed = now.Int64();
			User::After(Math::Rand(seed) % -(sendDbg().iSleepPeriod));
			}

		reply().iEM = sendDbg().iEM;
		Mem::Copy(reply().iPW, sendDbg().iPW, KMaxMediaPassword);
		}
	else
		{
		RenderPassword(&con, KPasswordLeft, pw);
		do
			{
			TConsoleKey key;

			con.Read(key);
			TInt keyCode = key.Code();

			switch (keyCode)
				{
				case EKeyEscape:
					em = EMPEMCancel;
					done = ETrue;
					break;

				case EKeyEnter:
					em = EMPEMUnlock;
					done = ETrue;
					break;
				
				case EKeySpace:
					em = EMPEMUnlockAndStore;
					done = ETrue;
					break;
				
				case EKeyBackspace:
					if (pw.Length() > 0)
						{
						pw.SetLength(pw.Length() - 1);
						RenderPassword(&con, KPasswordLeft, pw);
						}
					break;

				default:							// interpret other keys as pw contents
					TChar ch(keyCode);
					// unicode encoding, so number of password characters is half byte length
					if (ch.IsPrint() && pw.Length() < KMaxMediaPassword / 2)
						{
						pw.Append(ch);
						RenderPassword(&con, KPasswordLeft, pw);
						}
					break;
				}
			} while (! done);

		// Fill the TMediaPswdReplyNotifyInfoV1 structure.

		if (em == EMPEMUnlock || em == EMPEMUnlockAndStore)
			{
			const TInt byteLen = pw.Length() * 2;

			// zero entire array; and then copy in valid section of TMediaPassword,
			// not converting Unicode to ASCII.
			TPtr8 pt8(reply().iPW, KMaxMediaPassword);	// length = 0
			pt8.FillZ(KMaxMediaPassword);				// length = KMaxMediaPassword
			pt8.Zero();									// length = 0
														// length = byteLen
			pt8.Copy(reinterpret_cast<const TUint8 *>(pw.Ptr()), byteLen);
			}
		
		// Set exit mode to tell user how dialog handled.

		reply().iEM = em;
		}	// else (sendInfoLen == sizeof(TMediaPswdSendNotifyInfoV1Debug))
	
	con.Destroy();

	aMsg.WriteL(2, reply);
	}

//
// class MNotifierBase2
//

void MNotifierBase2::SetManager(MNotifierManager* aManager)
	{
	iManager=aManager;
	}

//
// class CNotifierServer
//

_LIT(__NOTIFIER_SERVER,"TextNotifierSrvr");

CNotifierServer* CNotifierServer::NewL()
	{
	CNotifierServer* server=new(ELeave) CNotifierServer(200);
	CleanupStack::PushL(server);
	server->ConstructL();
	server->StartL(__NOTIFIER_NAME);
	CleanupStack::Pop(); // server
	return server;
	}

CNotifierServer::~CNotifierServer()
	{
	SetIsExiting();
	delete iManager;
	}

void CNotifierServer::SetIsExiting()
	{
	iExiting=ETrue;
	}

TBool CNotifierServer::IsExiting() const
	{
	return iExiting;
	}

CNotifierServer::CNotifierServer(TInt aPriority)
	: CServer2(aPriority)
	{}

void CNotifierServer::ConstructL()
	{
	iManager=CNotifierManager::NewL();
	RFs fs;
	User::LeaveIfError(fs.Connect());
	CleanupClosePushL(fs);
	iManager->RegisterL(fs);
	CleanupStack::PopAndDestroy(); // fs.Close()
	}

CSession2* CNotifierServer::NewSessionL(const TVersion &aVersion,const RMessage2&) const
	{
	TVersion v(1,0,0); // !! liaise with E32
	if (!User::QueryVersionSupported(v,aVersion))
		User::Leave(KErrNotSupported);
	return new(ELeave) CNotifierSession(*this);
	}

//
// class CNotifierSession
//

CNotifierSession::CNotifierSession(const CNotifierServer& aServer)
	{
	iServer=&aServer;
	iClientId=(TInt)this;
	}

CNotifierSession::~CNotifierSession()
	{
	const CNotifierServer* server=static_cast<const CNotifierServer*>(Server());
	if (!server->IsExiting())
		{
		server->Manager()->HandleClientExit(iClientId);
		}
	}

void CNotifierSession::ServiceL(const RMessage2 &aMessage)
	{
	TBool completeMessage=ETrue;
	switch (aMessage.Function())
		{
	case ENotifierNotify:
		DisplayAlertL(aMessage);
		break;
	case ENotifierNotifyCancel:
		// do nothing - this server doesn't support cancelling RNotifier::Notify - the client will just have to wait
		break;
	case ENotifierInfoPrint:
		DisplayInfoMsgL(aMessage);
		break;
	case EStartNotifier:
		DoStartNotifierL(aMessage);
		break;
	case ECancelNotifier:
		iServer->Manager()->NotifierCancel(TUid::Uid(aMessage.Int0()));
		break;
	case EUpdateNotifierAndGetResponse:
	case EUpdateNotifier:
		DoUpdateNotifierL(aMessage);
		break;
	case EStartNotifierAndGetResponse:
		{
		if (aMessage.Int0()==KMediaPasswordNotifyUid)
			{
			RunPasswordWindowL(aMessage);
			}
		else
			{
			TBool cleanupComplete=ETrue;
			StartNotifierAndGetResponseL(aMessage,cleanupComplete);
			 // if the plug-in starts successfully, it has
			 // responsibility for completing the message (either
			 // synchronously or asynchronously)
			completeMessage=EFalse;
			}
		}
		break;
	default:
		aMessage.Complete(KErrNotSupported);
		break;
		}
	if (completeMessage && !aMessage.IsNull())
		{
		aMessage.Complete(KErrNone);
		}
	}

void CNotifierSession::DisplayAlertL(const RMessage2& aMessage)
	{
	const TInt lengthOfCombinedBuffer=User::LeaveIfError(aMessage.GetDesLength(1));
	const TInt lengthOfLine1=(STATIC_CAST(TUint,aMessage.Int2())>>16);
	const TInt lengthOfLine2=(aMessage.Int2()&KMaxTUint16);
	const TInt lengthOfBut1=(STATIC_CAST(TUint,aMessage.Int3())>>16);
	const TInt lengthOfBut2=(aMessage.Int3()&KMaxTUint16);
	if (lengthOfCombinedBuffer!=lengthOfLine1+lengthOfLine2+lengthOfBut1+lengthOfBut2)
		{
		PanicClient(aMessage,ENotifierPanicInconsistentDescriptorLengths);
		return;
		}
	HBufC* const combinedBuffer=HBufC::NewLC(lengthOfCombinedBuffer);
	{TPtr combinedBuffer_asWritable(combinedBuffer->Des());
	aMessage.ReadL(1,combinedBuffer_asWritable);}
	const TPtrC line1(combinedBuffer->Left(lengthOfLine1));
	const TPtrC line2(combinedBuffer->Mid(lengthOfLine1,lengthOfLine2));
	const TPtrC but1(combinedBuffer->Mid(lengthOfLine1+lengthOfLine2,lengthOfBut1));
	const TPtrC but2(combinedBuffer->Mid(lengthOfLine1+lengthOfLine2+lengthOfBut1,lengthOfBut2));
	TInt buttons, len, offset;
	if (lengthOfBut2==0)
		{
		buttons=1;
		len=lengthOfBut1+2;
		}
	else
		{
		buttons=2;
		len=lengthOfBut1+lengthOfBut2+5;
		}
	if (lengthOfLine1>len)
		len=lengthOfLine1;
	if (lengthOfLine2>len)
		len=lengthOfLine2;
	RConsole con;
	con.Create();
	TSize scsz;
	con.ScreenSize(scsz);
	con.Control(_L("-Visible"));
	TInt ww=Min(len,scsz.iWidth-4);
	TInt wh=3;
	if ((lengthOfBut1+lengthOfBut2+5)>ww)
		wh++;
	if (lengthOfLine1>ww)
		wh++;
	if (lengthOfLine2>ww)
		wh++;
	con.Init(_L(""),TSize(ww,wh));
	con.Control(_L("+Max -Cursor -Allowresize +Ontop +Wrap"));
	con.Write(line1);
	con.SetCursorPosAbs(TPoint(0,1));
	con.Write(line2);
	if (buttons==2)
		offset=(len-lengthOfBut1-lengthOfBut2-5)/2;
	else
		offset=(len-lengthOfBut1-2)/2;
	con.SetCursorPosAbs(TPoint(offset,2));
	con.Write(_L("<"));
	con.Write(but1);
	con.Write(_L(">"));
	if(buttons==2)
		{
		con.Write(_L(" <"));
		con.Write(but2);
		con.Write(_L(">"));
		}
	
	TConsoleKey key;
	TInt keycode;
	do
		{
		con.Read(key);
		keycode=key.Code();
		}
	while((keycode!=EKeyEscape&&keycode!=EKeyEnter&&buttons==2)||(keycode!=EKeyEnter&&buttons==1));
	if(keycode==EKeyEscape)
		keycode=0;
	else
		keycode=1;
	con.Destroy();
	aMessage.WriteL(0,TPckgC<TInt>(keycode));
	CleanupStack::PopAndDestroy(combinedBuffer);
	}

TInt CNotifierSession::InfoPrintThread(TAny* aMessage)
	{
	TBuf<0x50> des; // 0x50 max size of message
	RConsole con;
	des=*(TBuf<0x50> *)aMessage;
	TInt l=des.Length();
	NotifierSemaphore.Signal();
	con.Create();
	con.Control(_L("-Visible"));
	TSize size;
	con.ScreenSize(size);
	TInt ww=Min(size.iWidth-6,l);
	TInt wh=(l+ww-1)/ww;
	if (wh==0)
		wh=1;
	con.Init(_L(""),TSize(ww,wh));
	con.Control(_L("+Maximise"));
	con.SetWindowPosAbs(TPoint(size.iWidth-ww-4,1));
	con.Control(_L("+Wrap +Ontop"));
	con.Write(des);

	User::After(1300000);
	con.Destroy();
	return KErrNone;
	}

void CNotifierSession::DisplayInfoMsgL(const RMessage2& aMessage)
	{
	TInt r;
	TBuf<0x50> des; // 0x50 max size of message lines
	aMessage.ReadL(0,des);
	RThread thread;
	do
		{
		r=thread.Create(_L("Info Window"),InfoPrintThread,KDefaultStackSize,&User::Allocator(),(TAny*)&des);
		if(r==KErrAlreadyExists)
			User::After(200000);
		} while(r==KErrAlreadyExists);
		User::LeaveIfError(r);
	thread.Resume();
	NotifierSemaphore.Wait();
	thread.Close();
	}

void CNotifierSession::DoStartNotifierL(const RMessage2& aMessage)
	{
	const TUid targetUid={aMessage.Int0()};
	HBufC8* const inputBuffer=HBufC8::NewLC(User::LeaveIfError(aMessage.GetDesLength(1)));
	{TPtr8 input(inputBuffer->Des());
	aMessage.ReadL(1,input);}
	TPtrC8 output(0,0);
	iServer->Manager()->NotifierStartL(targetUid,*inputBuffer,&output,iClientId);
	if(aMessage.Int2())
		aMessage.WriteL(2,output);
	CleanupStack::PopAndDestroy(inputBuffer);
	}

void CNotifierSession::DoUpdateNotifierL(const RMessage2& aMessage)
	{
	const TUid targetUid={aMessage.Int0()};
	HBufC8* const inputBuffer=HBufC8::NewLC(User::LeaveIfError(aMessage.GetDesLength(1)));
	{TPtr8 input(inputBuffer->Des());
	aMessage.ReadL(1, input);}
	HBufC8* const outputBuffer=HBufC8::NewLC(User::LeaveIfError(aMessage.GetDesMaxLength(2)));
	{TPtr8 output(outputBuffer->Des());
	iServer->Manager()->NotifierUpdateL(targetUid,*inputBuffer,&output,iClientId);}
	aMessage.WriteL(2,*outputBuffer);
	CleanupStack::PopAndDestroy(2,inputBuffer);
	}

void CNotifierSession::StartNotifierAndGetResponseL(const RMessage2& aMessage,TBool& aCleanupComplete)
	{
	const TUid targetUid={aMessage.Int0()};
	HBufC8* const inputBuffer=HBufC8::NewLC(User::LeaveIfError(aMessage.GetDesLength(1)));
	{TPtr8 input(inputBuffer->Des());
	aMessage.ReadL(1,input);}
	iServer->Manager()->NotifierStartAndGetResponseL(targetUid,*inputBuffer,2,aMessage,iClientId,aCleanupComplete);
	CleanupStack::PopAndDestroy(inputBuffer);
	}

void CNotifierSession::PanicClient(const RMessage2& aMessage,TNotifierPanic aCode)
	{
	aMessage.Panic(__NOTIFIER_SERVER,aCode);
	}

//
// class CNotifierManager
//

const TInt KNullClientId=0;

CNotifierManager* CNotifierManager::NewL()
	{
	CNotifierManager* self=new(ELeave) CNotifierManager;
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CNotifierManager::~CNotifierManager()
	{
	if (iObservedList)
		{
		const TInt count=iObservedList->Count();
		for (TInt ii=0;ii<count;ii++)
			{
			(*iObservedList)[ii]->Release();
			}
		delete iObservedList;
		}
	if (iLibraries)
		{
		const TInt count=iLibraries->Count();
		for (TInt ii=0;ii<count;ii++)
			{
			(*iLibraries)[ii].Close();
			}
		delete iLibraries;
		}
	delete iChannelMonitor;
	delete iActivityMonitor;
	delete iQueue;
	}


void CNotifierManager::RegisterL(RFs& aFs)
	{
#ifdef SUPPORT_OLD_PLUGIN_PATH
	TBool old;
	for(old=0; old<2; old++)
	{
#endif
	TFindFile* findFile=new(ELeave) TFindFile(aFs);
	CleanupStack::PushL(findFile);
	TParse* fileNameParser=new(ELeave) TParse;
	CleanupStack::PushL(fileNameParser);
	CDir* directory=NULL;
	TInt error;
#ifdef SUPPORT_OLD_PLUGIN_PATH
	_LIT(KNotifierPlugInOldSearchPath,"\\system\\tnotifiers\\");
	if(old)
		error=findFile->FindWildByDir(KNotifierPlugInExt, KNotifierPlugInOldSearchPath, directory);
	else
#endif
	error=findFile->FindWildByDir(KNotifierPlugInExt, KNotifierPlugInSearchPath, directory);
	for (; error!=KErrNotFound; error=findFile->FindWild(directory))
		{
		CleanupStack::PushL(directory);
		User::LeaveIfError(error);
		const TInt numberOfEntries=directory->Count();
		for (TInt i=0; i<numberOfEntries; ++i)
			{
			const TEntry& entry=(*directory)[i];
			fileNameParser->SetNoWild(entry.iName, &findFile->File(), NULL); // findFile->File() returns a reference rather than an object, therefore taking the address of it is fine

			if (entry.iType[0].iUid==0x10000079)
				{
				 // It's a DLL
				if( (entry.iType[1]==KUidTextNotifierPlugInV2))
					{
					// Its a notifier...
					TPtrC path(fileNameParser->DriveAndPath());
					TPtrC name(fileNameParser->NameAndExt());
					DoAddPlugInL(path,name,entry.iType);
					}
				}
			}
		CleanupStack::PopAndDestroy(); // directory
		directory=NULL;
		}
	delete directory;
	CleanupStack::PopAndDestroy(2); // fileNameParser and findFile
#ifdef SUPPORT_OLD_PLUGIN_PATH
	}
#endif
	}

LOCAL_C void DeleteTemp(TAny* aPtr)
	{
	CArrayPtr<MNotifierBase2>* array=REINTERPRET_CAST(CArrayPtr<MNotifierBase2>*,aPtr);
	const TInt count=array->Count();
	for (TInt ii=0;ii<count;ii++)
		{
		(*array)[ii]->Release();
		}
	delete array;
	}



void CNotifierManager::DoAddPlugInL(const TDesC& aPath,const TDesC& aFileName,const TUidType& aUidType)
	{
	RLibrary lib;
	User::LeaveIfError(lib.Load(aFileName,aPath,aUidType));
	CleanupClosePushL(lib);
	iLibraries->AppendL(lib);
	CleanupStack::Pop(); // lib
	TLibraryFunction libEntry=lib.Lookup(1);
	CArrayPtr<MNotifierBase2>* array=REINTERPRET_CAST(CArrayPtr<MNotifierBase2>*,(libEntry)());
	User::LeaveIfNull(array);
	CleanupStack::PushL(TCleanupItem(DeleteTemp,array));
	while (array->Count()>0)
		{
		MNotifierBase2* notif=(*array)[0];
			{
			iObservedList->AppendL(notif);
			array->Delete(0);
			notif->SetManager(this);
			}
		MNotifierBase2::TNotifierInfo info=notif->RegisterL();
		if (!iChannelMonitor->AlreadyHasChannel(info.iChannel))
			iChannelMonitor->AddNewChannelL(info.iChannel);
		}
	CleanupStack::PopAndDestroy(); // array
	}

CNotifierManager::CNotifierManager()
	{}

void CNotifierManager::ConstructL()
	{
	iObservedList=new(ELeave) CArrayPtrSeg<MNotifierBase2>(6);
	iLibraries=new(ELeave) CArrayFixFlat<RLibrary>(2);
	iChannelMonitor=CChannelMonitor::NewL();
	iActivityMonitor=CActivityMonitor::NewL();
	iQueue=CNotifierQueue::NewL();
	}

struct SActivityCleanup
	{
	CActivityMonitor* iMonitor;
	TUid iNotifier;
	TInt iClientId;
	};

LOCAL_C void CleanupActivityMonitor(TAny* aPtr)
	{
	SActivityCleanup& cleanup=*REINTERPRET_CAST(SActivityCleanup*,aPtr);
	cleanup.iMonitor->Remove(cleanup.iNotifier,cleanup.iClientId);
	}

void CNotifierManager::NotifierStartL(TUid aNotifierUid,const TDesC8& aBuffer,TPtrC8* aResponse,TInt aClientId)
	{
	TInt result=KErrNotFound;
	const TInt count=iObservedList->Count();
	for (TInt ii=0;ii<count;ii++)
		{
		MNotifierBase2* notif=(*iObservedList)[ii];
		MNotifierBase2::TNotifierInfo info=notif->Info();
		if (info.iUid==aNotifierUid)
			{
			if (iActivityMonitor->IsNotifierActive(aNotifierUid,info.iChannel))
				{
				result=KErrAlreadyExists;
				}
			else if (info.iPriority>iChannelMonitor->ActivityLevel(info.iChannel))
				{
				TUid notifier;
				MNotifierBase2::TNotifierPriority priority;
				const TBool channelWasActive=iActivityMonitor->IsChannelActive(info.iChannel,notifier,priority);
				iActivityMonitor->AddL(info,aClientId);
				SActivityCleanup cleanup;
				cleanup.iMonitor=iActivityMonitor;
				cleanup.iNotifier=aNotifierUid;
				cleanup.iClientId=aClientId;
				CleanupStack::PushL(TCleanupItem(CleanupActivityMonitor,&cleanup));
				TPtrC8 response(notif->StartL(aBuffer));
				if(aResponse)
					aResponse->Set(response);
				CleanupStack::Pop(); // cleanup;
				if (channelWasActive)
					{
					for (TInt jj=0;jj<count;jj++)
						{
						MNotifierBase2* notifForUpdate=(*iObservedList)[ii];
						MNotifierBase2::TNotifierInfo infoForUpdate=notifForUpdate->Info();
						if (infoForUpdate.iUid==notifier && infoForUpdate.iChannel==info.iChannel)
							{
							TRAP_IGNORE(notifForUpdate->UpdateL(KNotifierPaused));
							}
						}
					}
				iChannelMonitor->UpdateChannel(info.iChannel,info.iPriority);
				if (result!=ENotExtRequestQueued)
					{
					result=ENotExtRequestCompleted;
					}
				}
			else
				{
				if (iQueue->IsAlreadyQueued(info.iUid,info.iChannel))
					{
					result=KErrAlreadyExists;
					}
				else
					{
					CQueueItem* queueCopy=CQueueItem::NewL(info,aBuffer,aClientId);
					CleanupStack::PushL(queueCopy);
					iQueue->QueueItemL(queueCopy);
					CleanupStack::Pop(); // queueCopy
					result=ENotExtRequestQueued;
					}
				}
			}
		}
	User::LeaveIfError(result);
	}

TInt CNotifierManager::NotifierUpdateL(TUid aNotifierUid,const TDesC8& aBuffer,TDes8* aResponse,TInt aClientId)
	{
	TInt result=KErrNotFound;
	const TInt count=iObservedList->Count();
	for (TInt ii=0;ii<count;ii++)
		{
		MNotifierBase2* notif=(*iObservedList)[ii];
		MNotifierBase2::TNotifierInfo info=notif->Info();
		if (info.iUid==aNotifierUid)
			{
			if (iActivityMonitor->IsNotifierActive(aNotifierUid,info.iChannel))
				{
				if (!iActivityMonitor->IsClientPresent(aNotifierUid,info.iChannel,aClientId))
					{
					iActivityMonitor->AddL(info,aClientId);
					}
				if (aResponse==NULL)
					{
					notif->UpdateL(aBuffer);
					}
				else
					{
					aResponse->Copy(notif->UpdateL(aBuffer));
					}
				}
			else
				{
				; // not all channels have been started yet so update the queue
				}
			result=KErrNone;
			}
		}
	return result;
	}

void CNotifierManager::NotifierStartAndGetResponseL(TUid aNotifierUid,const TDesC8& aBuffer,TInt aReplySlot,
														  const RMessage2& aMessage,TInt aClientId,TBool& aCleanupComplete)
	{
	NotifierStartAndGetResponseL(aNotifierUid,TUid::Null(),aBuffer,aReplySlot,aMessage,aClientId,aCleanupComplete);
	}

void CNotifierManager::NotifierStartAndGetResponseL(TUid aNotifierUid,TUid aChannelUid,const TDesC8& aBuffer,TInt aReplySlot,
														  const RMessage2& aMessage,TInt aClientId,TBool& aCleanupComplete)
	{
	TInt result=KErrNotFound;
	const TInt count=iObservedList->Count();
	for (TInt ii=0;ii<count;ii++)
		{
		MNotifierBase2* notif=(*iObservedList)[ii];
		MNotifierBase2::TNotifierInfo info=notif->Info();
		if (info.iUid==aNotifierUid && (aChannelUid==TUid::Null() || info.iChannel==aChannelUid))
			{
			if (iActivityMonitor->IsNotifierActive(aNotifierUid,info.iChannel))
				{
				notif->StartL(aBuffer,aReplySlot,aMessage); // asynch notifier can decide whether to support multiple clients
				result=KErrNone;
				}
			else if (info.iPriority>iChannelMonitor->ActivityLevel(info.iChannel))
				{
				TUid notifier;
				MNotifierBase2::TNotifierPriority priority;
				const TBool channelWasActive=iActivityMonitor->IsChannelActive(info.iChannel,notifier,priority);
				iActivityMonitor->AddL(info,aClientId);
				SActivityCleanup activityCleanup;
				activityCleanup.iMonitor=iActivityMonitor;
				activityCleanup.iNotifier=aNotifierUid;
				activityCleanup.iClientId=aClientId;
				CleanupStack::PushL(TCleanupItem(CleanupActivityMonitor,&activityCleanup));
				aCleanupComplete=EFalse;
				// IMPORTANT, aMessage needs to be a full RMessage object until suport for V1 notifiers is removed
				// I.e. until CNotifierBaseAdaptor is removed
				notif->StartL(aBuffer,aReplySlot,aMessage);
				CleanupStack::Pop(&activityCleanup);
				if (channelWasActive)
					{
					for (TInt jj=0;jj<count;jj++)
						{
						MNotifierBase2* notifForUpdate=(*iObservedList)[ii];
						MNotifierBase2::TNotifierInfo infoForUpdate=notifForUpdate->Info();
						if (infoForUpdate.iUid==notifier && infoForUpdate.iChannel==info.iChannel)
							{
							TRAP_IGNORE(notifForUpdate->UpdateL(KNotifierPaused));
							}
						}
					}
				iChannelMonitor->UpdateChannel(info.iChannel,info.iPriority);
				result=KErrNone;
				}
			else
				{
				if (iQueue->IsAlreadyQueued(info.iUid,info.iChannel))
					{
					result=KErrAlreadyExists;
					}
				else
					{
					CQueueItem* queueCopy=CQueueItem::NewL(info,aBuffer,aReplySlot,aMessage,aClientId);
					CleanupStack::PushL(queueCopy);
					iQueue->QueueItemL(queueCopy);
					CleanupStack::Pop(queueCopy);
					result=ENotExtRequestQueued;
					}
				}
			}
		}
	User::LeaveIfError(result);
	}

TInt CNotifierManager::NotifierCancel(TUid aNotifierUid)
	{
	TInt result=KErrNotFound;
	const TInt count=iObservedList->Count();
	for (TInt ii=0;ii<count;ii++)
		{
		MNotifierBase2* notif=(*iObservedList)[ii];
		MNotifierBase2::TNotifierInfo info=notif->Info();
		if (info.iUid==aNotifierUid)
			{
			notif->Cancel();
			iActivityMonitor->RemoveNotifier(aNotifierUid,info.iChannel);
			MNotifierBase2::TNotifierPriority priority=MNotifierBase2::ENotifierPriorityLowest;
			TUid notifier;
			//check channel activity and get highest priority on channnel
			if (iActivityMonitor->IsChannelActive(info.iChannel,notifier,priority))
				{

				//check if priority of a queued item on the same channel is
				//greater 
				MNotifierBase2::TNotifierPriority queuePriority=
				(MNotifierBase2::TNotifierPriority)iQueue->GetHighestQueuePriority(info.iChannel);
				if (queuePriority>priority)
					{
					iChannelMonitor->UpdateChannel(info.iChannel,MNotifierBase2::ENotifierPriorityLowest);
					CQueueItem* next=iQueue->FetchItem(info.iChannel);
					if (next)
						{
						TUid notif=next->iInfo.iUid;
						TRAPD(err,StartFromQueueL(next));
						if (err!=KErrNone)
							{
							NotifierCancel(notif);
							}
						}
					 }
					else
					{
					for (TInt jj=0;jj<count;jj++)
						{
						MNotifierBase2* notifForUpdate=(*iObservedList)[ii];
						MNotifierBase2::TNotifierInfo infoForUpdate=notifForUpdate->Info();
						if (infoForUpdate.iUid==notifier && infoForUpdate.iChannel==info.iChannel)
							{
							TRAP_IGNORE(notifForUpdate->UpdateL(KNotifierPaused));
							}
						}
					iChannelMonitor->UpdateChannel(info.iChannel,priority);
					}
				}
			else
				{
				iChannelMonitor->UpdateChannel(info.iChannel,MNotifierBase2::ENotifierPriorityLowest);
				CQueueItem* next=iQueue->FetchItem(info.iChannel);
				if (next)
					{
					TUid notif=next->iInfo.iUid;
					TRAPD(err,StartFromQueueL(next));
					if (err!=KErrNone)
						{
						NotifierCancel(notif);
						}
					}
				}
			result=KErrNone;
			}
		}
	return result;
	}

struct SCleanupMessage
	{
	TBool* iDoCleanup;
	RMessage2* iMessage;
	};

LOCAL_C void CleanupStartAndGetResponse(TAny* aPtr)
	{
	SCleanupMessage& cleanup=*REINTERPRET_CAST(SCleanupMessage*,aPtr);
	if (cleanup.iDoCleanup)
		{
		cleanup.iMessage->Complete(KErrNoMemory);
		}
	}

void CNotifierManager::StartFromQueueL(CQueueItem* aItem)
	{
	CleanupStack::PushL(aItem);
	TPtr8 buffer=aItem->iBuffer->Des();
	if (aItem->iAsynchronous)
		{
		SCleanupMessage cleanup;
		TBool doCleanup=ETrue;
		cleanup.iDoCleanup=&doCleanup;
		cleanup.iMessage=&aItem->iMessage;
		CleanupStack::PushL(TCleanupItem(CleanupStartAndGetResponse,&cleanup));
		// IMPORTANT, aItem->iMessage needs to be a full RMessage object until suport for V1 notifiers is removed
		// I.e. until CNotifierBaseAdaptor is removed
		NotifierStartAndGetResponseL(aItem->iInfo.iUid,aItem->iInfo.iChannel,buffer,aItem->iReplySlot,aItem->iMessage,aItem->iClientId,doCleanup);
		CleanupStack::Pop(&cleanup);
		}
	else
		{
		NotifierStartL(aItem->iInfo.iUid,buffer,NULL,aItem->iClientId);
		}
	CleanupStack::PopAndDestroy(); // aItem
	CQueueItem* update=iQueue->FetchItem(aItem->iInfo.iChannel);
	while (update)
		{
		CleanupStack::PushL(update);
		NotifierUpdateL(update->iInfo.iUid,*update->iBuffer,NULL,update->iClientId);
		CleanupStack::PopAndDestroy(); // update
		update=iQueue->FetchItem(aItem->iInfo.iChannel);
		}
	}

void CNotifierManager::HandleClientExit(TInt aClientId)
	{
	TUid notifier=KNullUid;
	while (iActivityMonitor->NotifierForClient(notifier,aClientId))
		{
		const TInt count=iObservedList->Count();
		for (TInt ii=0;ii<count;ii++)
			{
			MNotifierBase2* notif=(*iObservedList)[ii];
			if (notif->Info().iUid==notifier)
				{
				NotifierCancel(notifier);
				}
			}
		iActivityMonitor->Remove(notifier,aClientId);
		}
	iActivityMonitor->RemoveClient(aClientId);
	iQueue->RemoveClient(aClientId);
	}

void CNotifierManager::StartNotifierL(TUid aNotifierUid,const TDesC8& aBuffer,TDes8& aResponse)
	{
	TPtrC8 response(0,0);
	NotifierStartL(aNotifierUid,aBuffer, &response,KNullClientId);
	aResponse.Copy(response);
	}

void CNotifierManager::CancelNotifier(TUid aNotifierUid)
	{
	NotifierCancel(aNotifierUid);
	}

void CNotifierManager::UpdateNotifierL(TUid aNotifierUid,const TDesC8& aBuffer,TDes8& aResponse)
	{
	NotifierUpdateL(aNotifierUid,aBuffer,&aResponse,KNullClientId);
	}

//
// class CChannelMonitor
//

CChannelMonitor* CChannelMonitor::NewL()
	{
	CChannelMonitor* self=new(ELeave) CChannelMonitor;
	return self;
	}

TBool CChannelMonitor::AlreadyHasChannel(TUid aChannel)const
	{
	const TInt count=iMonitor.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		if (iMonitor[ii].iChannel==aChannel)
			return ETrue;
		}
	return EFalse;
	}

TInt CChannelMonitor::ActivityLevel(TUid aChannel) const
	{
	const TInt count=iMonitor.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		TChannelActivity activity=iMonitor[ii];
		if (activity.iChannel==aChannel)
			return activity.iHighestPriorityRunning;
		}
	return 0;
	}

void CChannelMonitor::UpdateChannel(TUid aChannel,TInt aLevel)
	{
	const TInt count=iMonitor.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		TChannelActivity& activity=iMonitor[ii];
		if (activity.iChannel==aChannel)
			{
			activity.iHighestPriorityRunning=aLevel;
			break;
			}
		}
	}

CChannelMonitor::CChannelMonitor()
	:iMonitor(3)
	{}

//
// class CNotifierActivity
//

CNotifierActivity* CNotifierActivity::NewLC(const MNotifierBase2::TNotifierInfo& aInfo,TInt aClientId)
	{ // static
	CNotifierActivity* self=new(ELeave) CNotifierActivity(aInfo);
	CleanupStack::PushL(self);
	self->ConstructL(aClientId);
	return self;
	}

CNotifierActivity::~CNotifierActivity()
	{
	iClientArray.Reset();
	}

TInt CNotifierActivity::Find(TInt aClientId) const
	{
	TInt index=KErrNotFound;
	const TInt count=iClientArray.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		TInt clientId=iClientArray[ii];
		if (clientId==aClientId)
			{
			index=ii;
			break;
			}
		}
	return index;
	}

CNotifierActivity::CNotifierActivity(const MNotifierBase2::TNotifierInfo& aInfo)
	: iInfo(aInfo), iClientArray(1)
	{}

void CNotifierActivity::ConstructL(TInt aClientId)
	{
	iClientArray.AppendL(aClientId);
	}

//
// class CActivityMonitor
//

CActivityMonitor* CActivityMonitor::NewL()
	{ // static
	CActivityMonitor* self=new(ELeave) CActivityMonitor();
	return self;
	}

CActivityMonitor::~CActivityMonitor()
	{
	iMonitor.ResetAndDestroy();
	}

void CActivityMonitor::AddL(const MNotifierBase2::TNotifierInfo& aInfo,TInt aClientId)
	{
	const TInt index=Find(aInfo.iUid,aInfo.iChannel);
	if (index==KErrNotFound)
		{
		CNotifierActivity* activity=CNotifierActivity::NewLC(aInfo,aClientId);
		iMonitor.AppendL(activity);
		CleanupStack::Pop(); // activity
		}
	else
		{
		iMonitor[index]->iClientArray.AppendL(aClientId);
		}
	}

void CActivityMonitor::Remove(TUid aNotifierUid,TInt aClientId)
	{
	const TInt index=Find(aNotifierUid);
	if (index!=KErrNotFound)
		{
		CNotifierActivity* activity=iMonitor[index];
		const TInt clientIndex=activity->Find(aClientId);
		if (clientIndex!=KErrNotFound)
			{
			if (activity->iClientArray.Count()==1)
				{
				delete activity;
				iMonitor.Delete(index);
				}
			else
				{
				activity->iClientArray.Delete(index);
				}
			}
		}
	}

void CActivityMonitor::RemoveNotifier(TUid aNotifierUid,TUid aChannel)
	{
	const TInt index=Find(aNotifierUid,aChannel);
	if (index!=KErrNotFound)
		{
		delete iMonitor[index];
		iMonitor.Delete(index);
		}
	}

void CActivityMonitor::RemoveClient(TInt aClientId)
	{
	TInt ii=0;
	while (ii<iMonitor.Count())
		{
		CNotifierActivity* ptr=iMonitor[ii];
		TInt index=ptr->Find(aClientId);
		if (index!=KErrNotFound)
			{
			ptr->iClientArray.Delete(index);
			}
		if (ptr->iClientArray.Count()==0)
			{
			iMonitor.Delete(ii);
			}
		else
			{
			++ii;
			}
		}
	}

TBool CActivityMonitor::IsNotifierActive(TUid aNotifierUid,TUid aChannel) const
	{
	const TInt index=Find(aNotifierUid,aChannel);
	return (index!=KErrNotFound);
	}

TBool CActivityMonitor::IsClientPresent(TUid aNotifierUid,TUid aChannel,TInt aClientId) const
	{
	TBool found=EFalse;
	const TInt index=Find(aNotifierUid,aChannel);
	if (index!=KErrNotFound)
		{
		found=(iMonitor[index]->Find(aClientId)!=KErrNotFound);
		}
	return found;
	}

TBool CActivityMonitor::IsChannelActive(TUid aChannel,TUid& aNotifier,MNotifierBase2::TNotifierPriority& aHighestPriority) const
	{
	TBool ret=EFalse;
	const TInt count=iMonitor.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		MNotifierBase2::TNotifierInfo info=iMonitor[ii]->iInfo;
		if (info.iChannel==aChannel)
			{
			ret=ETrue;
			if ((MNotifierBase2::TNotifierPriority)info.iPriority>aHighestPriority)
				{
				aNotifier=info.iUid;
				aHighestPriority=(MNotifierBase2::TNotifierPriority)info.iPriority;
				}
			}
		}
	return ret;
	}

TBool CActivityMonitor::NotifierForClient(TUid& aNotifierUid,TInt aClientId) const
	{
	TBool isOnlyClient=EFalse;
	aNotifierUid=KNullUid;
	const TInt count=iMonitor.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		CNotifierActivity* ptr=iMonitor[ii];
		if (ptr->Find(aClientId)!=KErrNotFound)
			{
			aNotifierUid=ptr->iInfo.iUid;
			isOnlyClient=ptr->iClientArray.Count()==1;
			break;
			}
		}
	return isOnlyClient;
	}

CActivityMonitor::CActivityMonitor()
	: iMonitor(1)
	{}

TInt CActivityMonitor::Find(TUid aNotifierUid) const
	{
	TInt index=KErrNotFound;
	const TInt count=iMonitor.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		if (iMonitor[ii]->iInfo.iUid==aNotifierUid)
			{
			index=ii;
			break;
			}
		}
	return index;
	}

TInt CActivityMonitor::Find(TUid aNotifierUid,TUid aChannel) const
	{
	TInt index=KErrNotFound;
	const TInt count=iMonitor.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		CNotifierActivity* ptr=iMonitor[ii];
		if (ptr->iInfo.iUid==aNotifierUid && ptr->iInfo.iChannel==aChannel)
			{
			index=ii;
			break;
			}
		}
	return index;
	}

//
// class CQueueItem
//

CQueueItem* CQueueItem::NewL(const MNotifierBase2::TNotifierInfo& aInfo,const TDesC8& aBuffer,
									TInt aReplySlot,const RMessage2& aMessage,TInt aClientId) //Asynchronous
	{
	CQueueItem* self=new(ELeave) CQueueItem(aInfo);
	CleanupStack::PushL(self);
	self->ConstructL(aBuffer,aMessage,aClientId,aReplySlot);
	CleanupStack::Pop(); // self
	return self;
	}

CQueueItem* CQueueItem::NewL(const MNotifierBase2::TNotifierInfo& aInfo,const TDesC8& aBuffer,TInt aClientId) //synchronous
	{
	CQueueItem* self=new(ELeave) CQueueItem(aInfo);
	CleanupStack::PushL(self);
	self->ConstructL(aBuffer,aClientId);
	CleanupStack::Pop(); // self
	return self;
	}

CQueueItem::~CQueueItem()
	{
	delete iBuffer;
	}

CQueueItem::CQueueItem(const MNotifierBase2::TNotifierInfo& aInfo)
	: iInfo(aInfo)
	{}

void CQueueItem::ConstructL(const TDesC8& aBuffer,TInt aClientId)
	{
	iBuffer=aBuffer.AllocL();
	iClientId=aClientId;
	iAsynchronous=EFalse;
	}

void CQueueItem::ConstructL(const TDesC8& aBuffer,const RMessage2& aMessage,TInt aClientId,TInt aReplySlot)
	{
	iBuffer=aBuffer.AllocL();
	iAsynchronous=ETrue;
	iMessage=aMessage;
	iClientId=aClientId;
	iReplySlot=aReplySlot;
	}

//
// class CNotifierQueue
//

CNotifierQueue* CNotifierQueue::NewL()
	{
	CNotifierQueue* self=new(ELeave) CNotifierQueue;
	return self;
	}

CQueueItem* CNotifierQueue::FetchItem(TUid aChannel)
	{
	CQueueItem* result=NULL;
	const TInt count=iQueue.Count();
	TInt priority=MNotifierBase2::ENotifierPriorityLowest-1;
	TInt index=KErrNotFound;
	for (TInt ii=0;ii<count;ii++)
		{
		CQueueItem* item=iQueue[ii];
		if (item->iInfo.iChannel==aChannel && item->iInfo.iPriority>priority)
			{
			index=ii;
			priority=item->iInfo.iPriority;
			result=item;
			}
		}
	if (index!=KErrNotFound)
		{
		iQueue.Delete(index);
		}
	return result;
	}

TBool CNotifierQueue::IsAlreadyQueued(TUid aNotifier,TUid aChannel) const
	{
	TBool ret=EFalse;
	const TInt count=iQueue.Count();
	for (TInt ii=0;ii<count;ii++)
		{
		CQueueItem* item=iQueue[ii];
		if (item->iInfo.iUid==aNotifier && item->iInfo.iChannel==aChannel)
			{
			ret=ETrue;
			break;
			}
		}
	return ret;
	}

void CNotifierQueue::RemoveClient(TInt aClientId)
	{
	const TInt count=iQueue.Count();
	for (TInt ii=count-1;ii>=0;ii--)
		{
		CQueueItem* item=iQueue[ii];
		TInt clientId=item->iClientId;
		if (clientId==aClientId)
			{
			iQueue.Delete(ii);
			}
		}
	}


TInt CNotifierQueue::GetHighestQueuePriority(TUid aChannel)
	{
	const TInt count=iQueue.Count();
	TInt priority=MNotifierBase2::ENotifierPriorityLowest-1;

	for (TInt ii=0;ii<count;ii++)
		{
		CQueueItem* item=iQueue[ii];
		if (item->iInfo.iChannel==aChannel && item->iInfo.iPriority>priority)
			{
			priority=item->iInfo.iPriority;
			}
		}

	return priority;
	}


void CWsActiveScheduler::New()
//
// Create and install the active scheduler.
//
	{

	CWsActiveScheduler *pA=new CWsActiveScheduler;
	__ASSERT_ALWAYS(pA!=NULL,Fault(ECreateScheduler));
	CActiveScheduler::Install(pA);
	}

void CWsActiveScheduler::Error(TInt) const
//
// Called if any Run() method leaves.
//
	{
	}


TInt NotifierServerThread(TAny*)
	{
	CTrapCleanup* CleanUpStack=CTrapCleanup::New();
	CWsActiveScheduler::New();
	TRAP_IGNORE(CNotifierServer::NewL());
	CNotifierSession::NotifierSemaphore.Signal();
	CWsActiveScheduler::Start();
	delete CleanUpStack;
	return(0);
	}


_LIT(KLitKeyDataDllNameBase, "EKDATA");
_LIT(TwoDigExt,".%02d");

GLDEF_C TInt E32Main()
	{
	UserSvr::WsRegisterThread();
	UserSvr::WsRegisterSwitchOnScreenHandling(ETrue);
	User::SetProcessCritical(User::ESystemPermanent);
	User::SetCritical(User::ESystemPermanent);

	CWsActiveScheduler::New();
	CWsServer::New();
	CWsWindow::New();
	CEvent::New();
    
	KeyTranslator=CKeyTranslator::New();
	if (!KeyTranslator)
		Fault(ENoKeyboardTranslator);

//  Change keyboard mapping according to information in the HAL
//	This code is the same as WSERV
	TInt keyboardIndex;
	if (HAL::Get(HALData::EKeyboardIndex,keyboardIndex)==KErrNone)
		{
		TBuf<16> keyDataDllName(KLitKeyDataDllNameBase);
		keyDataDllName.AppendFormat(TwoDigExt, keyboardIndex);
		KeyTranslator->ChangeKeyData(keyDataDllName);
		}

    KeyRepeat=new(ELeave) CKeyRepeat(CKeyRepeat::EKeyRepeatPriority);
	TRAPD(r,KeyRepeat->ConstructL());
	if (r!=KErrNone)
		User::Panic(_L("KEYREPEAT"),r);

#ifndef __WINS__
    if (UserSvr::TestBootSequence())
		{
		RDebug::Print(_L("WS_MAIN: TestBootSequence=TRUE, not loading ESHELL.EXE"));
		}
#else
    if (EmulatorAutoRun())
    	{	// don't start ESHELL if we used a self-bootstrapping EXE
    	}
#endif
	else
		{
		RProcess shell;
		r=shell.Create(KShellProcessName, KShellCommandLine);
		__ASSERT_ALWAYS(r==KErrNone,Fault(ECreateShell));
		shell.Resume();
		shell.Close();
		}

	RThread t;
	r=CNotifierSession::NotifierSemaphore.CreateLocal(0);
	if (r!=KErrNone)
		Fault(ECreateNotifierSemaphore);
	r=t.Create(_L("NotifierServer"),NotifierServerThread,KDefaultStackSize,0x2000,0x100000,NULL);
	if (r!=KErrNone)
		Fault(ECreateNotifierThread);
	t.Resume();
	CNotifierSession::NotifierSemaphore.Wait();

	CWsActiveScheduler::Start();
	UserSvr::ReleaseEventHook();
	return(KErrNone);
	}