sl@0: /*
sl@0: * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: * All rights reserved.
sl@0: * This component and the accompanying materials are made available
sl@0: * under the terms of the License "Eclipse Public License v1.0"
sl@0: * which accompanies this distribution, and is available
sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: *
sl@0: * Initial Contributors:
sl@0: * Nokia Corporation - initial contribution.
sl@0: *
sl@0: * Contributors:
sl@0: *
sl@0: * Description: 
sl@0: * Generic client-side code for client-server interaction.
sl@0: * Attempts to establish a connection to a session counting server,
sl@0: * starting the process if required.
sl@0: *
sl@0: */
sl@0: 
sl@0: 
sl@0: /**
sl@0:  @file
sl@0: */
sl@0: 
sl@0: #include <scs/scsclient.h>
sl@0: 
sl@0: #include <scs/scscommon.h>
sl@0: 
sl@0: using namespace ScsImpl;
sl@0: 
sl@0: 
sl@0: EXPORT_C RScsClientBase::RScsClientBase()
sl@0: /**
sl@0: 	This constructor is protected to ensure this class is not
sl@0: 	instantiated directly.
sl@0:  */
sl@0: :	RSessionBase()
sl@0: 	{
sl@0: 	// empty.
sl@0: 	}
sl@0: 
sl@0: EXPORT_C void RScsClientBase::Close()
sl@0: /**
sl@0: 	This method should be used in preference to RScsSessionBase::Close
sl@0: 	because it sends a message to cancel any outstanding requests on the
sl@0: 	session or its subsessions.
sl@0:  */
sl@0: 	{
sl@0: 	if(iHandle)
sl@0: 		{
sl@0: 		RSessionBase::SendReceive(EPreCloseSession);
sl@0: 		RSessionBase::Close();
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: TInt RScsClientBase::StartServerProcess(const TDesC& aExeName, const TUidType& aFullExeUid)
sl@0: /**
sl@0: 	This function is defined for the convenience of subclasses which need to start
sl@0: 	a server process before they can connect to the server.
sl@0: 
sl@0: 	@param	aExeName		Executable which hosts the server.
sl@0: 	@param	aFullExeUid		The server executable's full UID.  This is used to ensure the
sl@0: 							intended executable is started, and not another executable
sl@0: 							with the same name.
sl@0: 	@return					Symbian OS error code.  KErrNone indicates success, and any other
sl@0: 							value indicates failure.
sl@0: 	@pre This function should only be called by Connect(const TVersion&) if the server is
sl@0: 		not already running.
sl@0:  */
sl@0: 	{
sl@0: 	RProcess pr;
sl@0: 	TInt r = pr.Create(aExeName, /* aCommand */ KNullDesC, aFullExeUid);
sl@0: 	if (r != KErrNone)
sl@0: 		return r;
sl@0: 
sl@0: 	TRequestStatus rs;
sl@0: 	pr.Rendezvous(rs);
sl@0: 	if (rs != KRequestPending)
sl@0: 		r = rs.Int();
sl@0: 	else
sl@0: 		{
sl@0: 		pr.Resume();
sl@0: 		User::WaitForRequest(rs);
sl@0: 		if (rs.Int()==KErrAlreadyExists)
sl@0: 			r=KErrAlreadyExists;
sl@0: 		else
sl@0: 			r = (pr.ExitType() == EExitPending) ? rs.Int() : KErrGeneral;
sl@0: 		}
sl@0: 
sl@0: 	pr.Close();
sl@0: 	return r;
sl@0: 	}
sl@0: 
sl@0: EXPORT_C TInt RScsClientBase::Connect(
sl@0: 	const TDesC& aSvrName, const TVersion& aReqVer, const TDesC& aExeName, const TUidType& aFullExeUid)
sl@0: /**
sl@0: 	Attempt to connect to the named server.  If the server is not available then attempt
sl@0: 	to start its hosting process.
sl@0: 
sl@0: 	@param	aSvrName		Name of server to connect to.
sl@0: 	@param	aReqVer			Required server version.
sl@0: 	@param	aExeName		Executable which hosts the server.  This function will launch this
sl@0: 							executable if the server is not running.
sl@0: 	@param	aFullExeUid		The server executable's full UID.  This ensures the intended
sl@0: 							executable is started, and not another executable with the same name.
sl@0: 	@return					Symbian OS error code.  KErrNone indicates success,
sl@0: 							and any other value indicates failure.
sl@0:  */
sl@0: 	{
sl@0: 	TInt retries = 2;		// number of remaining retries
sl@0: 
sl@0: 	for (;;)
sl@0: 		{
sl@0: 		TInt r = CreateSession(aSvrName, aReqVer);
sl@0: 		
sl@0: 		// if connected then finished
sl@0: 		if (r == KErrNone)
sl@0: 			return r;
sl@0: 
sl@0: 		// if any reason other than server not available then abort
sl@0: 		if (r != KErrNotFound && r != KErrServerTerminated)
sl@0: 			return r;
sl@0: 
sl@0: 		if (--retries == 0)
sl@0: 			return r;
sl@0: 
sl@0: 		r = StartServerProcess(aExeName, aFullExeUid);
sl@0: 		if (r != KErrNone && r != KErrAlreadyExists)
sl@0: 			return r;
sl@0: 		}	// for (;;)
sl@0: 	}
sl@0: 
sl@0: // -------- server heap checking --------
sl@0: 
sl@0: EXPORT_C TInt RScsClientBase::SetServerHeapFail(TInt aRate)
sl@0: /**
sl@0: 	Start marking the server heap and set a deterministic
sl@0: 	fail rate.  This should matched with a call to EndServerHeapFail.
sl@0: 	
sl@0: 	This function is empty in release builds.
sl@0: 	
sl@0: 	@param	aRate			Number of allocations after which allocation
sl@0: 							should fail on the server heap.
sl@0: 	@see EndServerHeapFail
sl@0: 	@see __UHEAP_MARK
sl@0: 	@see __UHEAP_SETFAIL
sl@0:  */
sl@0: 	{
sl@0: #ifndef _DEBUG
sl@0: 	(void) aRate;
sl@0: 	return KErrNone;
sl@0: #else
sl@0: 	TIpcArgs ipc(aRate);
sl@0: 	return RSessionBase::SendReceive(ScsImpl::EUHeapSetFail, ipc);
sl@0: #endif
sl@0: 	}
sl@0: 
sl@0: EXPORT_C TInt RScsClientBase::ResetServerHeapFail()
sl@0: /**
sl@0: 	Finish marking the server heap and reset the failure rate.
sl@0: 	This should match a previous call to SetServerHeapFail.
sl@0: 
sl@0: 	If there is a heap imbalance, then the server will be panicked.
sl@0: 
sl@0: 	This function is empty in release builds.
sl@0: 	
sl@0: 	@see SetServerHeapFail
sl@0: 	@see __UHEAP_MARKEND
sl@0: 	@see __UHEAP_RESET
sl@0:  */
sl@0: 	{
sl@0: #ifdef _DEBUG
sl@0: 	return RSessionBase::SendReceive(ScsImpl::EUHeapResetFail);	
sl@0: #else
sl@0: 	return KErrNone;
sl@0: #endif
sl@0: 	}
sl@0: 
sl@0: // -------- passing arguments to a server-side session --------
sl@0: 
sl@0: EXPORT_C TInt RScsClientBase::ShutdownServer()
sl@0: /**
sl@0: DEBUG USE ONLY - Tells the server to shutdown down ASAP, and block
sl@0: until it has done so. This also closes the current session.
sl@0: 
sl@0: If the server is not configured to use a inactivity shutdown timer,
sl@0: this will fail with KErrNotSupported.
sl@0: 
sl@0: nb. You may still need to call the Close function of a derived class
sl@0: to ensure it gets to cleanup...
sl@0: 
sl@0: 	@return					Symbian OS error code where KErrNone indicates
sl@0: 							success and any other value indicates failure.
sl@0:  */
sl@0: 	{
sl@0: 	// Find servers PID
sl@0: 	TPckgBuf<TProcessId> idBuf;
sl@0: 	TIpcArgs args(&idBuf);
sl@0: 	TInt r = RSessionBase::SendReceive(ScsImpl::EGetServerPid, args);
sl@0: 	if(r != KErrNone) return r;
sl@0: 
sl@0: 	// Open a handle for the server thread
sl@0: 	RProcess server;
sl@0: 	r = server.Open(idBuf(), EOwnerThread);
sl@0: 	if(r != KErrNone) return r;
sl@0: 	
sl@0: 	// Logon to the server process to spot when it exits
sl@0: 	TRequestStatus rs;
sl@0: 	server.Logon(rs);
sl@0: 
sl@0: 	// Ask the server to exit ASAP
sl@0: 	r = RSessionBase::SendReceive(ScsImpl::EShutdownAsap);
sl@0: 	if(r != KErrNone)
sl@0: 		{
sl@0: 		(void) server.LogonCancel(rs);
sl@0: 		server.Close();
sl@0: 		return r;
sl@0: 		}
sl@0: 
sl@0: 	// Close our session
sl@0: 	Close();
sl@0: 
sl@0: 	// Wait for the server to finish shutting down
sl@0: 	User::WaitForRequest(rs); // nb. we do not care what code it shutdown with.
sl@0: 
sl@0: 	// Close our server process handle
sl@0: 	server.Close();
sl@0: 
sl@0: 	return KErrNone;
sl@0: 	}
sl@0: 
sl@0: // -------- passing arguments to a server-side session --------
sl@0: 
sl@0: EXPORT_C TInt RScsClientBase::CallSessionFunction(TInt aFunction) const
sl@0: /**
sl@0: 	Send a command to the corresponding server-side session.  The
sl@0: 	subclass uses this function instead of directly calling
sl@0: 	RSubSessionBase::SendReceive because it adds the SCS code
sl@0: 	which marks this as an ordinary session call.
sl@0: 	
sl@0: 	@param	aFunction		Function identifier.  Bits 31:24 must be zero,
sl@0: 							because they are reserved for SCS commands.
sl@0: 	@return					Error code with which the server completed the request.
sl@0:  */
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(! ScsFieldUsed(aFunction), ClientSidePanic(EScsClNoArgsSessUsedScs));
sl@0: 	
sl@0: 	TInt f = ECallSessionFunc | aFunction;
sl@0: 	return RSessionBase::SendReceive(f);
sl@0: 	}
sl@0: 
sl@0: EXPORT_C TInt RScsClientBase::CallSessionFunction(TInt aFunction, const TIpcArgs& aArgs) const
sl@0: /**
sl@0: 	Send a command to the corresponding server-side session.  The
sl@0: 	subclass uses this function instead of directly calling
sl@0: 	RSubSessionBase::SendReceive because it adds the SCS code which
sl@0: 	marks this as an ordinary session call.
sl@0: 
sl@0: 	@param	aFunction		Session function identifier.  Bits 31:24 must be zero,
sl@0: 							because they are reserved for SCS commands.
sl@0: 	@param	aArgs			Standard IPC arguments.
sl@0: 	@return					Error code with which the server completed the request.
sl@0:  */
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(! ScsFieldUsed(aFunction), ClientSidePanic(EScsClArgsSessUsedScs));
sl@0: 	
sl@0: 	TInt f = ECallSessionFunc | aFunction;
sl@0: 	return RSessionBase::SendReceive(f, aArgs);
sl@0: 	}
sl@0: 
sl@0: EXPORT_C void RScsClientBase::CallSessionFunction(TInt aFunction, const TIpcArgs& aArgs, TRequestStatus& aStatus) const
sl@0: /**
sl@0: 	Send the supplied function identifier and arguments to the server-side
sl@0: 	session.  The subclass uses this function instead of directly calling
sl@0: 	RSubSessionBase::SendReceive because it adds the SCS code which marks
sl@0: 	this as an ordinary session call.
sl@0: 
sl@0: 	@param	aFunction		Session function identifier.  Bits 31:24 must be zero,
sl@0: 							because they are reserved for SCS commands.
sl@0: 	@param	aArgs			Standard IPC arguments.
sl@0: 	@param	aStatus			This will be completed by the server when it has
sl@0: 							finished handling the function.
sl@0:  */
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(! ScsFieldUsed(aFunction), ClientSidePanic(EScsClArgsSessAsyncUsedScs));
sl@0: 	
sl@0: 	TInt f = ECallSessionFunc | aFunction;
sl@0: 	RSessionBase::SendReceive(f, aArgs, aStatus);
sl@0: 	}
sl@0: 
sl@0: EXPORT_C void RScsClientBase::CancelSessionFunction(TInt aFunction) const
sl@0: /**
sl@0: 	Cancel an outstanding session request.  This has no effect if the
sl@0: 	request is not outstanding.
sl@0: 	
sl@0: 	@param	aFunction		Implementation function.  This must be the
sl@0: 							same value that was supplied to CallSessionFunction.
sl@0:  */
sl@0: 	{
sl@0: 	__ASSERT_DEBUG(! ScsFieldUsed(aFunction), ClientSidePanic(EScsClCancelSessUsedScs));
sl@0: 	
sl@0: 	TInt f = ECancelSessionFunc | aFunction;
sl@0: 	RSessionBase::SendReceive(f);
sl@0: 	}
sl@0: