// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// The Implementation of the CEComServer singleton class which
// instantiates an instance of the requested ECom Interface Implementation.
//
//
/**
@internalComponent
@file
*/
#include <e32base.h>
#include <bautils.h>
#include <startupdomaindefs.h>
#include "EComDebug.h"
#include <ecom/ecom.h>
#include <ecom/ecomerrorcodes.h>
#include <ecom/ecomresolverparams.h>
#include <ecom/implementationinformation.h>
#include "ServerStartupManager.h"
#include "RegistryData.h"
#include "Registrar.h"
#include "DefaultResolver.h"
#include "RomOnlyResolver.h"
#include "TlsData.h"
#include "EComServerStart.h"
#include "EComMessageIds.h"
#include "EComServerSession.h"
#include "EComServer.h"
#include "EComUidCodes.h"
#include "EComPerformance.h"
#include "DriveInfo.h"
#include "FileUtils.h"
#include "RegistryResolveTransaction.h"
#include "resolvercache.h"
#include "EComPatchDataConstantv2.h"
#define UNUSED_VAR(a) a = a
/** enum for special system events */
enum TSpecialEvents
{
EBURInProgress,
ESWIInProgress
};
/** The location of server INI file. */
_LIT(KEComSrvrIniFile,"_:\\private\\10009D8F\\EComSrvr.ini");
/** Buffer descriptor to hold full path and name of server INI file. */
typedef TBuf<32> TEComSrvrIniFileName;
_LIT(KEComSrvrIniFileROM,"Z:\\private\\10009D8F\\EComSrvr.ini");
static void CloseAndDeleteImplInfoArray(TAny* aObject)
{
RImplInfoArray* array=reinterpret_cast<RImplInfoArray*>(aObject);
if (array)
{
array->Close();
}
delete array;
}
//
// Initiate server exit when the timer expires
// by stopping the local scheduler.
void CShutdown::RunL()
{
CActiveScheduler::Stop();
}
CEComServer* CEComServer::NewLC()
{
// Standard 2 phase construction code
CEComServer* instance = new(ELeave) CEComServer;
CleanupStack::PushL(instance);
instance->ConstructL();
return instance;
}
CEComServer::~CEComServer()
{
// Uninstall callbacks first. Do not want to receive any callback
// in destructor. TCallBackWithArg constructed with no argument
// is a null callback.
TCallBackWithArg nullCallback;
if (iRegistrar)
{
iRegistrar->InstallSwiEventCallBack(nullCallback);
iRegistrar->InstallBurEventCallBack(nullCallback);
}
if (iRegistryData)
{
iRegistryData->SetImplUpgradeCallBack(nullCallback);
}
// Ensure this deletion order is maintained - in particular the registrydata
// must last longer than the others which hold a reference to it.
//remove the CServerStartupMgr
delete iServerStartupMgr;
// Then destroy the registrar object
delete iRegistrar;
// remove the registry data
delete iRegistryData;
delete iResolverCache;
// Finally close down the file server connection
iFs.Close();
// Make sure the non-default resolver library is closed
iResolverLibrary.Close();
}
CEComServer::CEComServer()
: CServer2(CActive::EPriorityStandard)
{
// Do nothing
}
void CEComServer::ConstructL()
{
START_TIMER
START_HEAP
StartL(KEComServerName);
// Connect to the file server
User::LeaveIfError(iFs.Connect());
__ECOM_TRACE("ECOM: Server INIT - File Server session initialised");
// construct the registry data handling object here
iRegistryData = CRegistryData::NewL(iFs);
__ECOM_TRACE("ECOM: Server INIT - Registry Data initialised");
// Then the registrar
iRegistrar = CRegistrar::NewL(*iRegistryData, *this, iFs);
__ECOM_TRACE("ECOM: Server INIT - Registrar initialised");
//Then the CServerStartupMgr
#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
iServerStartupMgr = new(ELeave) CServerStartupMgr(KDmHierarchyIdStartup, KSM2OSServicesDomain3, iFs);
#else
iServerStartupMgr = new(ELeave) CServerStartupMgr(KDmHierarchyIdStartup, KBaseServicesDomain3, iFs);
#endif //SYMBIAN_SYSTEM_STATE_MANAGEMENT
__ECOM_TRACE("ECOM: Server INIT - ServerStartupMgr initialised");
iServerStartupMgr->RegisterObserverL(iRegistrar);
iServerStartupMgr->InitialiseL(IsSSA(iFs));
iResolverCache = CCustomResolverCache::NewL(KCustomResolverCacheSize,
KCustomResolverCacheTimeout);
TCallBackWithArg eventCallback(&CEComServer::NotifyEvents, this);
iRegistrar->InstallSwiEventCallBack(eventCallback);
iRegistrar->InstallBurEventCallBack(eventCallback);
iRegistryData->SetImplUpgradeCallBack(eventCallback);
// The server is about to start so construct the transient shutdown guy
iShutdown.ConstructL();
// ensure that the server still exits even
// if the server start fails or the
// 1st client fails to connect
iShutdown.Start();
RECORD_INITIALISE_HEAP
RECORD_INITIALISE_RESULT
}
TBool CEComServer::IsSSA(RFs& aFs)
{
// Check Z drive first.
TBool isFileExisting = BaflUtils::FileExists(aFs, KEComSrvrIniFileROM);
// Check system drive next
if(!isFileExisting)
{
TEComSrvrIniFileName iniFile(KEComSrvrIniFile);
TEComFileUtils::SetToDrive(iniFile,iRegistryData->iSystemDrive);
isFileExisting = BaflUtils::FileExists(aFs, iniFile);
}
return !isFileExisting;
}
// Get implementation info for one implementation, and return to client
void CEComServer::GetImplementationInformationL(const TUid& aImplementationUid,
CImplementationInformation*& aImplInfo,
const TClientRequest& aClientRequest)
{
TEntry dllInfo;
User::LeaveIfError(iRegistryData->GetImplementationDllInfoForClientL(
aClientRequest, aImplementationUid, KNullUid, dllInfo, aImplInfo, ETrue));
}
RImplInfoArray* CEComServer::ListImplementationsL(TUid aInterfaceUid,
const TEComResolverParams& aAdditionalParameters,
TUid aResolverUid,
const RExtendedInterfacesArray& aExtendedInterfaces,
const TClientRequest& aMessage
)
{
RImplInfoArray* result = NULL;
CResolver* resolver = NULL;
//create registry resolver transaction
//get the TListImplParam parameters
TBool capability= ETrue;
if(!(aMessage.IsNull()))
{
TListImplParam listParam;
TPckg<TListImplParam> listParamPkg(listParam);
aMessage.ReadL(2,listParamPkg);
capability=listParam.iCapabilityCheck;
}
CRegistryResolveTransaction* registryResolveTransaction = CRegistryResolveTransaction::NewL(*iRegistryData,
aExtendedInterfaces,
aMessage,capability);
CleanupStack::PushL(registryResolveTransaction);
//create resolver
resolver = CreateResolverLC(aResolverUid,registryResolveTransaction);
result = ListImplementationsL(aInterfaceUid, aAdditionalParameters, resolver);
//clean up
CleanupStack::PopAndDestroy(resolver);
CleanupStack::PopAndDestroy(registryResolveTransaction);
if ((aResolverUid != KDefaultResolverUid) && (aResolverUid != KRomOnlyResolverUid))
{
iResolverLibrary.Close();
}
return result;
}
RImplInfoArray* CEComServer::ListImplementationsL(TUid aInterfaceUid,
TUid aResolverUid,
const RExtendedInterfacesArray& aExtendedInterfaces,
const TClientRequest& aMessage
)
{
RImplInfoArray* result = NULL;
CResolver* resolver = NULL;
//create registry resolver transaction
//get the TListImplParam parameters
//get the TListImplParam parameters
TBool capability= ETrue;
if(!(aMessage.IsNull()))
{
TListImplParam listParam;
TPckg<TListImplParam> listParamPkg(listParam);
aMessage.ReadL(2,listParamPkg);
capability=listParam.iCapabilityCheck;
}
CRegistryResolveTransaction* registryResolveTransaction = CRegistryResolveTransaction::NewL(*iRegistryData,
aExtendedInterfaces,
aMessage,capability);
CleanupStack::PushL(registryResolveTransaction);
//create resolver
resolver = CreateResolverLC(aResolverUid,registryResolveTransaction);
result = ListImplementationsL(aInterfaceUid, resolver);
//clean up
CleanupStack::PopAndDestroy(resolver);
CleanupStack::PopAndDestroy(registryResolveTransaction);
if ((aResolverUid != KDefaultResolverUid) && (aResolverUid != KRomOnlyResolverUid))
{
iResolverLibrary.Close();
}
return result;
}
RImplInfoArray* CEComServer::ListImplementationsL(TUid aInterfaceUid,
const TEComResolverParams& aAdditionalParameters,
const RExtendedInterfacesArray& aExtendedInterfaces,
const TClientRequest& aMessage)
{
// Use the default resolver in the overloaded method.
return ListImplementationsL(aInterfaceUid, aAdditionalParameters, KDefaultResolverUid,aExtendedInterfaces, aMessage);
}
RImplInfoArray* CEComServer::ListImplementationsL(TUid aInterfaceUid,
const RExtendedInterfacesArray& aExtendedInterfaces,
const TClientRequest& aMessage)
{
// Use the default resolver in the overloaded method.
return ListImplementationsL(aInterfaceUid, KDefaultResolverUid,aExtendedInterfaces, aMessage);
}
// The private helper
RImplInfoArray* CEComServer::ListImplementationsL(TUid aInterfaceUid,
const TEComResolverParams& aAdditionalParameters,
CResolver* aResolver) const
{
if(!aResolver)
User::Leave(KEComErrNoResolver);
// Use the client provided resolver to build up the list.
RImplInfoArray* infoArray = aResolver->ListAllL(aInterfaceUid, aAdditionalParameters);
return infoArray;
}
RImplInfoArray* CEComServer::ListImplementationsL(TUid aInterfaceUid,
CResolver* aResolver) const
{
if(!aResolver)
User::Leave(KEComErrNoResolver);
// Use the provided resolver to build up the list.
RImplInfoArray* infoArray = &aResolver->ListAllL(aInterfaceUid);
// infoArray points to iImplementationInfo, which is owned by CRegistryResolveTransaction.
// CRegistryResolveTransaction object is transient and will be destroyed before return the implementation
// info list to the CEComServerSession. Therefore, we need to have a copy to return
RImplInfoArray* retList = new (ELeave) RImplInfoArray;
CleanupStack::PushL(TCleanupItem(CloseAndDeleteImplInfoArray,retList));
const TInt numImps = infoArray->Count();
for(TInt index = 0; index < numImps; ++index)
{
retList->AppendL((*infoArray)[index]);
}
// Reset the member variable because we are passing ownership back
CleanupStack::Pop();
return retList;
}
void CEComServer::GetResolvedDllInfoL( const TUid aImplementationUid,
TEntry& aDllInfo,
TUid& aDtor_Key,
const TClientRequest& aClientRequest)
{
// No resolution to do create directly.
aDtor_Key = aImplementationUid;
CImplementationInformation* implInfo = NULL;
TUid dummyUid={0x00000000};
//We need to do the security check for the case that implementationUid is known.
//if implementationUid is unknown, the security check will be done in ListImplementationsL.
User::LeaveIfError(iRegistryData->GetImplementationDllInfoForClientL(
aClientRequest, aImplementationUid, dummyUid, aDllInfo, implInfo, ETrue));
}
void CEComServer::GetResolvedDllInfoL( const TUid aInterfaceUid,
const TEComResolverParams& aAdditionalParameters,
const RExtendedInterfacesArray& aExtendedInterfaces,
TEntry& aDllInfo,
TUid& aDtor_Key,
const TClientRequest& aClientRequest)
{
GetResolvedDllInfoL(aInterfaceUid, aAdditionalParameters, KDefaultResolverUid, aExtendedInterfaces, aDllInfo, aDtor_Key, aClientRequest);
}
void CEComServer::GetResolvedDllInfoL( const TUid aInterfaceUid,
const TEComResolverParams& aAdditionalParameters,
const TUid aResolverUid,
const RExtendedInterfacesArray& aExtendedInterfaces,
TEntry& aDllInfo,
TUid& aDtor_Key,
const TClientRequest& aClientRequest)
{
CResolver* resolver = NULL;
TBool capability= ETrue;
//create registry resolver transaction
CRegistryResolveTransaction* registryResolveTransaction = CRegistryResolveTransaction::NewL(*iRegistryData,
aExtendedInterfaces,
aClientRequest,capability);
CleanupStack::PushL(registryResolveTransaction);
//create resolver
resolver = CreateResolverLC(aResolverUid,registryResolveTransaction);
aDtor_Key = resolver->IdentifyImplementationL(aInterfaceUid, aAdditionalParameters);
//clean up
CleanupStack::PopAndDestroy(resolver);
CleanupStack::PopAndDestroy(registryResolveTransaction);
if ((aResolverUid != KDefaultResolverUid) && (aResolverUid != KRomOnlyResolverUid))
{
iResolverLibrary.Close();
}
CImplementationInformation* implInfo = NULL;
//Don't need to do the security check because it has been done in IdentifyImplementationL.
User::LeaveIfError(iRegistryData->GetImplementationDllInfoForClientL(
aClientRequest, aDtor_Key, aInterfaceUid, aDllInfo, implInfo, EFalse));
}
// Server Session management
CSession2* CEComServer::NewSessionL(const TVersion& aVersion,const RMessage2& /* aMessage*/) const
{
const TVersionName version = CONST_CAST(TVersion&,aVersion).Name();
const TVersionName thisVersion = TVersion(KEComServerMajorVN,KEComServerMinorVN,KEComServerBuildVN).Name();
if(thisVersion != version)
User::Leave(KErrNotSupported);
return new(ELeave) CEComServerSession();
}
//
// A new session is being created
// Cancel the shutdown timer if it was running
//
void CEComServer::AddSession()
{
++iSessionCount;
iShutdown.Cancel();
}
//
// A session is being destroyed
// Start the shutdown timer if it is the last session.
//
void CEComServer::DropSession()
{
if (--iSessionCount==0)
iShutdown.Start();
}
void CEComServer::Notification(TInt aCompletionCode)
//
// Pass on the signal to all clients
//
{
iSessionIter.SetToFirst();
CSession2* s;
while ((s = iSessionIter++)!=0)
STATIC_CAST(CEComServerSession*,s)->CompleteNotifications(aCompletionCode);
}
TInt CEComServer::RunError(TInt aError)
//
// Handle an error from CMySession::ServiceL()
// A bad descriptor error implies a badly programmed client, so panic it;
// otherwise report the error to the client
//
{
if (aError == KErrBadDescriptor)
{
PanicClient(Message(), aError);
}
else
{
Message().Complete(aError);
}
//
// The leave will result in an early return from CServer::RunL(), skipping
// the call to request another message. So do that now in order to keep the
// server running.
ReStart();
return KErrNone; // handled the error fully
}
/**
Creates resolver object with aResolverUid UID.
The method leaves resolver onto the cleanup stack.
@param aResolverUid Resolver UID.
@param aRegistryResolveTransaction A pointer to Registry resolve transaction object
@return A pointer to the created CResolver object.
@leave System-wide error codes, including KErrNoMemory.
*/
CResolver* CEComServer::CreateResolverLC(const TUid& aResolverUid,CRegistryResolveTransaction* aRegistryResolveTransaction)
{
CResolver* resolver = NULL;
if(aResolverUid == KDefaultResolverUid)
{
// Create default resolver
resolver = static_cast<CResolver*>(CDefaultResolver::NewL(*aRegistryResolveTransaction));
CleanupStack::PushL(resolver);
}
else if(aResolverUid == KRomOnlyResolverUid)
{
// Create Rom Only resolver
resolver = static_cast<CResolver*>(CRomOnlyResolver::NewL(*aRegistryResolveTransaction));
CleanupStack::PushL(resolver);
}
else
{
// Create Custom Resolver
resolver = CreateCustomResolverLC(aResolverUid, aRegistryResolveTransaction);
}
return resolver;
}
/**
Creates custom resolver object with aResolverUid UID.
The method leaves custom resolver onto the cleanup stack.
@param aResolverUid Custom resolver UID.
@param aRegistryResolveTransaction A pointer to Registry resolve transaction object
@return A pointer to the created CResolver object.
@leave System-wide error codes, including KErrNoMemory.
*/
CResolver* CEComServer::CreateCustomResolverLC(TUid aResolverUid,CRegistryResolveTransaction* aRegistryResolveTransaction)
{
typedef CResolver* (*TNewL)(MPublicRegistry&);
TNewL newL = NULL;
CResolver* resolver=NULL;
TProxyNewLPtr tmpPtr;
if (iResolverCache->CacheLookup(aResolverUid, tmpPtr)) // cache hit
{
newL = reinterpret_cast<TNewL>(tmpPtr);
resolver = newL(*aRegistryResolveTransaction);
CleanupStack::PushL(resolver);
return resolver;
}
TEntry resolverDllInfo;
// We should only load custom resolvers that are in the ROM
//Initialize the server cap to ProtServ
TCapabilitySet servercap(ECapabilityProtServ);
CImplementationInformation* implInfo = NULL;
TBool onWritableDrv = EFalse;
User::LeaveIfError(iRegistryData->GetImplementationDllInfoForServer(
servercap, aResolverUid, KEComResolverInterfaceUid, resolverDllInfo,
implInfo, onWritableDrv));
// Type of the function pointer which is the proxy into the interface implementation collection
typedef TImplementationProxy* (*TInstantiationL)(TInt&);
// Function at ordinal 1 is InstantiationMethodL()
const TInt KImplementationGroupProxy = 1;
// So cast to the correct type : This gives an ANSI C++ warning
// When using a REINTERPRET_CAST so simply cast instead
const TDesC& libraryPath = resolverDllInfo.iName;
// Make sure the non-default resolver library is closed
iResolverLibrary.Close();
User::LeaveIfError(iResolverLibrary.Load(libraryPath, resolverDllInfo.iType));
__ECOM_TRACE2("ECOM: Resolver Loaded UID:0x%X - %S", aResolverUid.iUid, &resolverDllInfo.iName);
TInstantiationL proxy= REINTERPRET_CAST(TInstantiationL, iResolverLibrary.Lookup(KImplementationGroupProxy));
// Scan the returned table for a UID match, and return the associated
// creation method if found.
TInt count = 0;
TImplementationProxy* implementationTable = proxy(count);
for(TInt i = 0; i < count; ++i)
{
if(aResolverUid == implementationTable[i].iImplementationUid)
{
newL = (TNewL)(implementationTable[i].iNewLFuncPtr);
}
}
if(newL)
{
if (IsCachable(onWritableDrv))
{
TUint32 flags = (onWritableDrv) ? EEntryIsOnReadWriteDrive : EEntryFlagsNone;
User::LeaveIfError(iResolverCache->CacheResolver(aResolverUid,
iResolverLibrary, (TProxyNewLPtr)newL, flags));
// Handle is now owned by iResolverCache.
iResolverLibrary.SetHandle(KNullHandle);
}
// Create the non-default resolver
resolver = newL(*aRegistryResolveTransaction);
CleanupStack::PushL(resolver);
}
else
{
User::Leave(KEComErrNoResolver);
}
return resolver;
}
TBool CEComServer::RegistryIndexValid() const
{
return iRegistryData->IndexValid();
}
/** Callback function. CRegistryData uses this to notify of implementation
upgrade. CDiscoverer uses this to notify state changes in SWI/BUR.
@param aObj Pointer to CEComServer object.
@param aEvent Identify the event.
@param aData Data associated with the callback.
@return none, not-used, ignored.
*/
TInt CEComServer::NotifyEvents(TAny* aObj, TInt aEvent, TAny* aData)
{
CEComServer* self = static_cast<CEComServer*>(aObj);
switch (aEvent)
{
case ECallBackId_ImplUpgrade:
{
TUid* uid = static_cast<TUid*>(aData);
self->NotifyUpgrade(*uid);
}
break;
case ECallBackId_SwiEvent:
self->NotifySWIEvent(aData);
break;
case ECallBackId_BurEvent:
self->NotifyBUREvent(aData);
break;
default:
__ECOM_TRACE1("ECOM: CEComServer::NotifyEvents received unknown event %d", aEvent);
}
return 0;
}
/** This method is called when an implementation is upgraded.
@param aImplementationUid identify the implementation being upgraded.
*/
void CEComServer::NotifyUpgrade(const TUid aImplementationUid)
{
// Ignore return code which indicates if the UID is actually in cache.
(void)iResolverCache->Remove(aImplementationUid);
}
/** Called when there is SWI status change.
@param aData is TCallBackState* indicating start or end of SWI.
*/
void CEComServer::NotifySWIEvent(TAny* aData)
{
TCallBackState* state = static_cast<TCallBackState*>(aData);
UpdateSpecialEvents(ESWIInProgress, *state);
}
/** Called when there is BUR status change.
@param aData is TCallBackState* indicating start or end of BUR.
*/
void CEComServer::NotifyBUREvent(TAny* aData)
{
TCallBackState* state = static_cast<TCallBackState*>(aData);
UpdateSpecialEvents(EBURInProgress, *state);
}
/** Updates the BUR/SWI status.
@param aBit Indicate which bit to update.
@param aState Indicate start or end of event.
*/
void CEComServer::UpdateSpecialEvents(TUint32 aBit, TCallBackState aState)
{
TBitFlags32 oldstate = iSpecialEvents;
if (aState == ECallBackState_EventStart)
{
iSpecialEvents.Set( aBit );
}
else
{
iSpecialEvents.Clear( aBit );
}
if (oldstate.Value() == 0 && iSpecialEvents.Value() != 0)
{
// BUR or SWI start. Need to evict cached resolvers on RW drives.
iResolverCache->RemoveItemsWithFlags(EEntryIsOnReadWriteDrive);
}
}
/** Determine if a resolver entry is cachable.
@param aResolverEntry the resolver to check.
@return ETrue if the resolver should be added to cache. EFalse otherwise.
*/
TBool CEComServer::IsCachable(TBool aEntryIsOnRWDrive)
{
// Check the following conditions:
// 1. DLL is on RW drive with BUR or SWI in progress.
// 2. Cache size and cache timeout non-zero.
return iResolverCache->CachingEnabled() &&
!(iSpecialEvents.Value() && aEntryIsOnRWDrive);
}
#ifdef __ECOM_SERVER_TESTABILITY__
void CEComServer::ChangeStartupStateL(TInt aState) const
{
iServerStartupMgr->ChangeStartupStateL(aState);
}
void CEComServer::ProcessCurrentStartupStateL() const
{
iServerStartupMgr->ResetRequestTransitionNotificationL();
iServerStartupMgr->ResetLastStateAcknowledgedL();
iServerStartupMgr->RunL();
}
TInt CEComServer::GetCurrentStartupState() const
{
return iServerStartupMgr->CurrentStartupState();
}
#endif //__ECOM_SERVER_TESTABILITY__
#ifdef __ECOM_SERVER_PERFORMANCE__
void CEComServer::GetRegistryCountsL(TInt aType, RegistryCounts::TRegistryCounts& aCounts) const
{
iRegistryData->GetRegistryCountsL(aType, aCounts);
}
#endif //__ECOM_SERVER_PERFORMANCE__