// SensibleClient.cpp
//
// Copyright (c) 2006 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//
#include "SensibleClient.h"
#include "SensibleClient_internals.h"
#include "cliserv.h"
#include "SensibleCompat.h"
_LIT(KClientPanic, "SensibleClient");
#define DebugPanic() User::Panic(KClientPanic, - __LINE__)
#define AssertPanic(x) User::Panic(KClientPanic, x)
enum TPanic
{
EReadOffEndOfCallback, // A call to TCallbackParser::Next when there's nothing more in the callback buffer
EBadType, // A call to TCallbackParser::NextX where X doesn't match the type of the next thing in the buffer
};
#ifdef EKA2
static TInt StartServer()
//
// Start the server process. Simultaneous launching
// of two such processes should be detected when the second one attempts to
// create the server object, failing with KErrAlreadyExists.
//
{
const TUidType serverUid(KNullUid,KNullUid,KServerUid3);
RProcess server;
TInt r=server.Create(KMyServerImg,KNullDesC,serverUid);
if (r!=KErrNone)
return r;
TRequestStatus stat;
server.Rendezvous(stat);
if (stat!=KRequestPending)
server.Kill(0); // abort startup
else
server.Resume(); // logon OK - start the server
User::WaitForRequest(stat); // wait for start or death
// we are expected to return a negative error code from this function so if the server died
// with a positive (or zero) code, map it to KErrServerTerminated
r = (server.ExitType() != EExitPending && stat.Int() >= 0) ? KErrServerTerminated : stat.Int();
server.Close();
return r;
}
#else
#include <e32math.h>
inline TServerStart::TServerStart(TRequestStatus& aStatus)
:iId(RThread().Id()),iStatus(&aStatus)
{aStatus=KRequestPending;}
inline TPtrC TServerStart::AsCommand() const
{return TPtrC(reinterpret_cast<const TText*>(this),sizeof(TServerStart)/sizeof(TText));}
static TInt StartServer()
//
// Start the server process/thread which lives in an EPOCEXE object
//
{
TRequestStatus started;
TServerStart start(started);
const TUidType serverUid(KNullUid,KNullUid,KServerUid3);
#ifdef __WINS__
//
// In WINS the server is a DLL, the exported entrypoint returns a TInt
// which represents the real entry-point for the server thread
//
RLibrary lib;
TInt r=lib.Load(KMyServerImg,serverUid);
if (r!=KErrNone)
return r;
TLibraryFunction ordinal1=lib.Lookup(1);
TThreadFunction serverFunc=reinterpret_cast<TThreadFunction>(ordinal1());
//
// To deal with the unique thread (+semaphore!) naming in EPOC, and that we may
// be trying to restart a server that has just exited we attempt to create a
// unique thread name for the server.
// This uses Math::Random() to generate a 32-bit random number for the name
//
TName name(KMyServerName);
name.AppendNum(Math::Random(),EHex);
RThread server;
r=server.Create(name,serverFunc,
KMyServerStackSize,
&start,&lib,NULL,
KMyServerInitHeapSize,KMyServerMaxHeapSize,EOwnerProcess);
lib.Close(); // if successful, server thread has handle to library now
#else
//
// EPOC is easy, we just create a new server process. Simultaneous launching
// of two such processes should be detected when the second one attempts to
// create the server object, failing with KErrAlreadyExists.
//
RProcess server;
TInt r=server.Create(KMyServerImg,start.AsCommand(),serverUid);
#endif
if (r!=KErrNone)
return r;
TRequestStatus died;
server.Logon(died);
if (died!=KRequestPending)
{
// logon failed - server is not yet running, so cannot have terminated
User::WaitForRequest(died); // eat signal
server.Kill(0); // abort startup
server.Close();
return died.Int();
}
//
// logon OK - start the server
server.Resume();
User::WaitForRequest(started,died); // wait for start or death
if (started==KRequestPending)
{
// server has died, never made it to the startup signal
server.Close();
return died.Int();
}
//
// server started (at last). Cancel and consume the death-notification
// before reporting success
server.LogonCancel(died);
server.Close();
User::WaitForRequest(died); // eat the signal (from the cancel)
return KErrNone;
}
TInt E32Dll(TDllReason)
{
return 0;
}
#endif
RSensibleSessionBody::RSensibleSessionBody()
: iDispatcher(NULL)
{}
TInt RSensibleSessionBody::Connect(TInt aMessageSlots, TBool aStartCallbackDispatcher)
//
// Connect to the server, attempting to start it if necessary
//
{
TInt r = KErrNone;
TInt retry=2;
for (;;)
{
r=DoCreateSession(KMyServerName, TVersion(0,0,0), aMessageSlots);
if (r!=KErrNotFound && r!=KErrServerTerminated)
break;
if (--retry==0)
break;
r=StartServer();
if (r!=KErrNone && r!=KErrAlreadyExists)
break;
}
if (r == KErrNone && aStartCallbackDispatcher)
{
if (StartCallbackDispatcher() != KErrNone)
{
Close();
return KErrNoMemory;
}
}
return r;
}
TInt RSensibleSessionBody::StartCallbackDispatcher()
{
if (!iDispatcher)
{
iDispatcher = new CServerCallbackDispatcher(*this);
}
else if (!iDispatcher->IsActive())
{
iDispatcher->Register();
}
return iDispatcher == NULL ? KErrNoMemory : KErrNone;
}
void RSensibleSessionBody::StopCallbackDispatcher()
{
if (iDispatcher) iDispatcher->Cancel();
}
void RSensibleSessionBody::DispatchCallbackL(TServerCallback& /*aCallback*/, TCallbackParser& /*aParser*/)
{
// Implemented by subclass
}
void RSensibleSessionBody::ServerDiedL()
{
// Implemented by subclass
}
void RSensibleSessionBody::Close()
{
delete iDispatcher;
iDispatcher = NULL;
}
// Not happy this actually works or is worth it
/*
void CCleanupAndComplete::RunL()
{
if (iClientStatus)
{
// We use this class with a null pointer to allow non-asynchronous (ie blind) APIs to still have the ability to cleanup resource when they are completed
User::RequestComplete(iClientStatus, iStatus.Int());
}
delete this;
}
void CCleanupAndComplete::DoCancel()
{
//TODO what? Probably delete this in some way
//delete this;
}
CCleanupAndComplete::CCleanupAndComplete(TRequestStatus* aClientStatus)
: CActive(EPriorityStandard), iClientStatus(aClientStatus)
{
CActiveScheduler::Add(this);
if (iClientStatus)
{
*iClientStatus = KRequestPending;
}
}
CCleanupAndComplete* CCleanupAndComplete::NewLC(TRequestStatus* aClientStatus)
{
CCleanupAndComplete* self = new(ELeave) CCleanupAndComplete(aClientStatus);
CleanupStack::PushL(self);
return self;
}
TInt CCleanupAndComplete::Create(TRequestStatus*& aStatus, const TDesC8& aDes, TInt& aIpcArg)
{
CCleanupAndComplete* self = new CCleanupAndComplete(aStatus);
TInt err = KErrNone;
if (!self) err = KErrNoMemory;
HBufC8* buf = NULL;
if (!err)
{
buf = aDes.Alloc();
}
if (!buf) err = KErrNoMemory;
if (!err)
{
err = self->iWithoutDestructor.Append(buf);
}
if (err)
{
delete buf;
delete self;
return KErrNoMemory;
}
aIpcArg = (TInt)buf;
aStatus = &self->iStatus;
self->SetActive();
return KErrNone;
}
TInt CCleanupAndComplete::Create(TRequestStatus*& aStatus, const TDesC16& aDes, TInt& aIpcArg)
{
CCleanupAndComplete* self = new CCleanupAndComplete(aStatus);
TInt err = KErrNone;
if (!self) err = KErrNoMemory;
HBufC16* buf = NULL;
if (!err)
{
buf = aDes.Alloc();
}
if (!buf) err = KErrNoMemory;
if (!err)
{
err = self->iWithoutDestructor.Append(buf);
}
if (err)
{
delete buf;
delete self;
return KErrNoMemory;
}
aIpcArg = (TInt)buf;
aStatus = &self->iStatus;
self->SetActive();
return KErrNone;
}
void CCleanupAndComplete::AddL(CBase* aWithDestructor)
{
User::LeaveIfError(iWithDestructor.Append(aWithDestructor));
}
void CCleanupAndComplete::AddL(TAny* aWithoutDestructor)
{
User::LeaveIfError(iWithoutDestructor.Append(aWithoutDestructor));
}
CCleanupAndComplete::~CCleanupAndComplete()
{
Cancel();
iWithDestructor.ResetAndDestroy();
for (TInt i = 0; i < iWithoutDestructor.Count(); i++)
{
delete iWithoutDestructor[i];
}
}
void CCleanupAndComplete::SetActive()
{
CActive::SetActive();
}
*/
CServerCallbackDispatcher::CServerCallbackDispatcher(RSensibleSessionBody& aSession)
: CActive(EPriorityStandard), iSession(aSession), iContextPtr(NULL, 0)
{
CActiveScheduler::Add(this);
Register();
}
void CServerCallbackDispatcher::Register()
{
iState = EWaitingForCallback;
IPC(args, &iNextCallback, 0,0,0);
iSession.DoSendReceive(ERegisterCallbackNotifier, args, iStatus);
SetActive();
}
void CServerCallbackDispatcher::RunL()
{
DISOWN(iCachedCallbackResult8);
DISOWN(iCachedCallbackResult16);
if (iStatus == KErrServerTerminated)
{
iSession.ServerDiedL();
return;
}
else if (iStatus != KErrNone)
{
//TODO Do something...
__DEBUGGER();
return;
}
TServerCallback cb = iNextCallback();
TCallbackParser p(cb);
if (iState == EWaitingForCallback && cb.iContextLength != 0)
{
iState = EWaitingForContext;
DISOWN(iContext);
iContext = HBufC8::NewL(cb.iContextLength);
iContextPtr = iContext->Des();
IPC(args, &iContextPtr, 0,0,0);
iSession.DoSendReceive(EGetCallbackContext, args, iStatus);
SetActive();
return;
}
if (iState == EWaitingForContext)
{
p.SetContext(*iContext);
}
Register();
iSession.DispatchCallbackL(cb, p);
}
void CServerCallbackDispatcher::DoCancel()
{
IPC(args, 0,0,0,0);
iSession.DoSendReceive(ECancelCallbackNotifier, args);
}
CServerCallbackDispatcher::~CServerCallbackDispatcher()
{
Cancel();
DISOWN(iCachedCallbackResult8);
DISOWN(iCachedCallbackResult16);
DISOWN(iContext);
}
TCallbackParser::TCallbackParser(const TServerCallback& aCallback)
: iCallback(aCallback), iContext(NULL), iNextPtr(aCallback.iData.Ptr()), iInContext(EFalse)
{}
void TCallbackParser::SetContext(TDesC8& aContext)
{
iContext = &aContext;
}
#define DOGET(T, type, name) T name; TAny* res = Next(sizeof(T), #type); Mem::Copy(&name, res, sizeof(T));
#define GET(T, type) DOGET(T, type, __result); return __result;
TInt TCallbackParser::NextInt()
{
GET(TInt, i);
}
TUint TCallbackParser::NextUint()
{
GET(TUint, u);
}
TPoint TCallbackParser::NextPoint()
{
GET(TPoint, P);
}
TSize TCallbackParser::NextSize()
{
GET(TSize, S);
}
TRgb TCallbackParser::NextRgb()
{
GET(TRgb, G);
}
TRect TCallbackParser::NextRect()
{
GET(TRect, R);
}
TPtrC TCallbackParser::NextDesC()
{
DOGET(TInt, i, len);
const TUint16* ptr = reinterpret_cast<const TUint16*>(Next(len*2, "D"));
if (TUint(ptr)&1)
{
// Need to allow for padding
ptr = (TUint16*)(((TUint8*)ptr)+1);
iNextPtr++; // And also move iNextPtr to the right place for the next read
}
return TPtrC(ptr, len);
}
TPtrC8 TCallbackParser::NextDesC8()
{
DOGET(TInt, i, len);
const TUint8* ptr = reinterpret_cast<const TUint8*>(Next(len, "8"));
return TPtrC8(ptr, len);
}
void* TCallbackParser::Next(TInt aSize, char* aType)
{
const TUint8* endPtr = iInContext ? iContext->Ptr() + iContext->Size() : iCallback.iData.Ptr() + iCallback.iData.Size();
if (iNextPtr + aSize + 1 > endPtr)
{
// No room for arg and type
if (!iInContext && iContext)
{
// try moving to context
iNextPtr = (TUint8*)iContext->Ptr();
iInContext = ETrue;
}
else
{
// Either there's no context, or we're already in it and reading off the end
AssertPanic(EReadOffEndOfCallback);
}
}
TUint8 nextType = *iNextPtr; //iInContext ? iCallback.iContextTypes[iIdx] : iCallback.iDataTypes[iIdx];
iNextPtr++;
void* result = (void*)iNextPtr;
__ASSERT_ALWAYS(nextType == *aType, AssertPanic(EBadType)); // Types must match
iNextPtr = iNextPtr + aSize;
return result;
}