hotspotfw/hsserver/src/hotspotserver.cpp
changeset 0 56b72877c1cb
child 2 6e4b6261703d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspotfw/hsserver/src/hotspotserver.cpp	Thu Dec 17 09:20:28 2009 +0200
@@ -0,0 +1,711 @@
+/*
+* Copyright (c) 2002-2006 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:       Implementation of HotSpot Plugin Server
+*
+*/
+
+
+
+
+
+
+// INCLUDE FILES
+#include <e32std.h>
+#include <f32file.h>
+#include <wlanmgmtclient.h>
+#include <wlanmgmtinterface.h>
+#include <e32std.h>
+#include <commsdattypesv1_1.h>
+
+#include <cmconnectionmethodext.h>
+#include <cmconnectionmethoddef.h>
+#include <cmpluginwlandef.h>
+#include <cmmanagerext.h>
+#include <cmmanagerdef.h>
+#include <cmdestinationext.h>
+
+#include "hotspotserver.h"
+#include "am_debug.h"
+#include "hotspotsession.h"
+#include "hssclientplugin.h"
+#include "hssiapsettingshandler.h"
+
+// LOCAL FUNCTION PROTOTYPES
+GLDEF_C TInt E32Main();
+
+using namespace CMManager;
+
+// CONSTANTS
+// Panic codes for HotSpot server
+const TInt KHotSpotPanicCleanupStackCreationFailed  = 1;
+const TInt KHotSpotPanicOpenSemaforeFailed          = 2;
+const TInt KHotSpotPanicCreationOfSchedulerFailed   = 3;
+
+// Default timeout values
+
+const TUint KMillion = 1000000;
+const TUint KHssDefaultLoginTimeMicroSecs = 180 * KMillion;  // 180 seconds (3 mins)
+const TUint KHssMinLoginTime =      5;   // 5 seconds
+const TUint KHssMaxLoginTime =      1200; // 1200 seconds (20 mins)
+const TUint KHssDefaultLogoutTimeMicroSecs  = 12 * KMillion;   // 12 seconds
+const TUint KHssMinLogoutTime =     1;    // 1 seconds
+const TUint KHssMaxLogoutTime =     30;   // 30 seconds
+
+// Panic category of HotSpot server 
+_LIT( KHotSpotModuleName, "HOTSPOTSRV" );
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// RunServerL
+// -----------------------------------------------------------------------------
+// 
+static void RunServerL()
+    {
+    User::LeaveIfError( User::RenameThread( KHotSpotServerName ) );
+
+    CActiveScheduler* s=new(ELeave) CActiveScheduler;
+    if ( !s )
+        {
+        User::Panic( KHotSpotModuleName, KHotSpotPanicCreationOfSchedulerFailed );
+        }
+    CleanupStack::PushL(s);
+    CActiveScheduler::Install(s);
+
+    CHotSpotServer::NewLC(); // Is NewLC: Server was pushed into cleanup stack.
+
+    RSemaphore started;
+    TInt err = started.CreateGlobal( KHotSpotServerSemaphore, 0 );
+    if ( err != KErrNone )
+        {
+        err = started.OpenGlobal( KHotSpotServerSemaphore );
+        }
+    
+    __ASSERT_ALWAYS(
+        err == KErrNone, 
+        User::Panic( KHotSpotModuleName, KHotSpotPanicOpenSemaforeFailed )
+        );
+        
+    // lets everyone know that the thread is ready to deal with requests            
+    RProcess::Rendezvous(KErrNone);
+        
+    started.Signal();
+    started.Close();
+
+    DEBUG("**** HotspotServer: server fully running");
+    CActiveScheduler::Start();
+    CleanupStack::PopAndDestroy( 2, s ); // Cleanup both server and scheduler
+    REComSession::FinalClose();
+    }
+
+// -----------------------------------------------------------------------------
+// PanicClient
+// -----------------------------------------------------------------------------
+//    
+void PanicClient(const RMessagePtr2& aMessage,THotspotPanic aPanic)
+	{
+	_LIT(KPanic,"HotspotServer");
+	aMessage.Panic(KPanic,aPanic);
+	}
+
+// ============================ MEMBER FUNCTIONS ===============================
+    
+// -----------------------------------------------------------------------------
+// CHotSpotServer
+// -----------------------------------------------------------------------------
+//
+CHotSpotServer::CHotSpotServer()
+	:CPolicyServer( EPriorityStandard, THotSpotServerPlatSecPolicy, ESharableSessions )
+    {
+	}
+
+// -----------------------------------------------------------------------------
+// NewLC
+// -----------------------------------------------------------------------------
+//
+CHotSpotServer* CHotSpotServer::NewLC()
+	{
+	DEBUG("**** HotSpotServer: CHotSpotServer::NewLC");
+	CHotSpotServer* self = new(ELeave) CHotSpotServer;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+// -----------------------------------------------------------------------------
+// ~CHotSpotServer
+// -----------------------------------------------------------------------------
+//
+CHotSpotServer::~CHotSpotServer()
+	{
+	DEBUG("**** HotSpotServer: CHotSpotServer::~CHotSpotServer");
+    
+    iMap.Close();
+    iNotificationArray.Close();
+    iLoginLogoutTimerArray.Close();
+    
+ 	if ( iMgtClient != NULL )
+        {
+#ifndef __WINS__	
+        iMgtClient->CancelNotifications();
+#endif
+        delete iMgtClient;
+        }
+ 	iMgtClient = NULL;
+ 	
+	}
+
+// -----------------------------------------------------------------------------
+// ConstructL
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::ConstructL()
+    {
+  
+    DEBUG("**** HotSpotServer: CHotSpotServer::ConstructL");
+	StartL( KHotSpotServerName );
+	
+	iIapCheckValue = EFalse;
+    iLogoutSent = EFalse;
+    iLoginValue = ETrue;
+    iAssociationValue = EFalse;
+	
+#ifndef __WINS__
+
+    // Change config daemon for Easy WLAN access point
+    iConfigDaemonChanged = KErrNone;
+    TBool retVal( EFalse );
+    iEasyWlanId = KEasyWlanServiceId; // Set to default value
+    TRAPD( leave, retVal = EasyWlanIdL() );
+    if ( KErrNone != leave )
+        {
+        iConfigDaemonChanged = leave;
+        }
+    if ( !retVal )
+        {
+        iConfigDaemonChanged = KErrGeneral;
+        }
+    DEBUG1("**** HotSpotServer: CHotSpotServer::ConstructL iConfigDaemonChanged: %d", iConfigDaemonChanged );
+#endif        
+    // Activate notifications for IAP check purposes
+    // When EWlanConnectionModeNotConnected is received we can cancel this and 
+    // we know that it safe to go through IAPs.
+    iMgtClient = CWlanMgmtClient::NewL();
+#ifndef __WINS__
+ 	iMgtClient->ActivateNotificationsL( *this );
+#endif 
+    }
+
+// -----------------------------------------------------------------------------
+// ConnectionStateChanged
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::ConnectionStateChanged( TWlanConnectionMode  aNewState ) 
+	{
+	DEBUG1( "CHotSpotServer::ConnectionStateChanged() aNewState=%d", aNewState );
+    if ( aNewState == EWlanConnectionModeNotConnected )
+    	{
+    	if ( iMgtClient != NULL )
+            {
+#ifndef __WINS__
+            iMgtClient->CancelNotifications();
+#endif
+            DEBUG("CHotSpotServer::ConnectionStateChanged2");
+            }
+    	
+     	TRAPD(err, CheckIapsL());
+     	if ( err != KErrNone )
+     		{
+     		DEBUG1("CHotSpotServer::ConnectionStateChanged(): %d", err);
+     		}
+     	if ( iConfigDaemonChanged != KErrNone )
+     	    {
+     	    // ConstructL call leaved. Let's call again once more.
+     	    TRAP_IGNORE( EasyWlanIdL() );
+     	    }
+     	}
+    }
+
+// -----------------------------------------------------------------------------
+// CheckIapsL
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::CheckIapsL()
+    {
+    DEBUG("CHotSpotServer::CheckIapsL");
+ 	 	
+ 	_LIT(KMarkFirst, "[" );
+ 	_LIT(KMarkLast, "]" );
+ 	// This is needed to be checked only once per boot
+	if ( iIapCheckValue == EFalse )
+	    {
+		iIapCheckValue = ETrue;
+		
+		RCmManagerExt cmManager;
+	    cmManager.OpenL();
+	    CleanupClosePushL(cmManager);
+
+	    TBool supportedBearersOnly = ETrue;
+	    TBool legacyCmsOnly = EFalse;
+	    
+	    RArray<TUint32> cmArray;
+        CleanupClosePushL( cmArray );
+	    
+	    cmManager.ConnectionMethodL( cmArray, supportedBearersOnly, legacyCmsOnly );
+	    DEBUG1("CHotSpotServer::CheckIapsL count: %d", cmArray.Count());  
+	    for( TInt i = 0; i < cmArray.Count(); i++ )
+	    	{
+	    	RCmConnectionMethodExt cm;
+		    TRAPD( err, cm = cmManager.ConnectionMethodL( cmArray[i] ) );
+		    DEBUG1("CHotSpotServer::CheckIapsL: err %d", err ); 
+		    if ( KErrNone == err )
+		    	{
+		    	HBufC* client( NULL );
+		    	TRAPD( errr, client = cm.GetStringAttributeL( EWlanServiceExtensionTableName ));
+		    	DEBUG1("CHotSpotServer::CheckIapsL: errr %d", errr );  
+		    	if( KErrNone == errr )
+		    		{
+		    		TBuf<KIapNameLength> clientUid;
+		    		TUid uid(TUid::Null());
+		    		
+		    		clientUid.Copy( client->Des() );
+                    delete client;
+		    		TInt indx = clientUid.Find( KMarkFirst );
+		    		if ( KErrNotFound != indx )
+						{
+						DEBUG("CHotSpotServer::CheckIapsL Client is found");
+						clientUid.Delete( indx, 1 );
+					    indx = clientUid.Find( KMarkLast );
+						if ( KErrNotFound != indx )
+							{
+							clientUid.Delete( indx, 1 );
+							}
+						// Convert TBuf to TUid
+						TLex lex( clientUid );
+						TUint value( 0 );
+						User::LeaveIfError( lex.Val( value, EHex ) );
+						uid.iUid = value; 
+						 	   		
+						// Try to find if 3rd party client exists.
+						// Delete IAP if no client.
+						CHssClientPlugin* plugin(NULL);
+						TBuf8<KExtensionAPILength> nullBuf;
+						TRAPD( error, plugin = CHssClientPlugin::NewL( uid, nullBuf ) );
+				        delete plugin;
+
+						DEBUG1("CHotSpotServer::CheckIapsL find client error: %d", error );
+						if ( error == KErrNotFound )
+							{
+		    	   			cm.DeleteL();
+							}
+		    	   		}
+		    		}
+		    	}
+		    DEBUG("CHotSpotServer::CheckIapsLOK");
+	    	}
+        CleanupStack::PopAndDestroy( &cmArray );
+        CleanupStack::PopAndDestroy( &cmManager );
+		}
+	DEBUG("CHotSpotServer::CheckIapsL Done");
+    }
+
+// -----------------------------------------------------------------------------
+// NewSessionL
+// -----------------------------------------------------------------------------
+//
+CSession2* CHotSpotServer::NewSessionL( const TVersion& aVersion, 
+                                        const RMessage2& /* aMessage */ ) const
+    {
+	TVersion version( KHotSpotMajorVersionNumber,
+                      KHotSpotMinorVersionNumber,
+                      KHotSpotBuildVersionNumber );
+
+    if ( !User::QueryVersionSupported( version, aVersion ) )
+        {
+        User::Leave( KErrNotSupported );
+        }
+
+	DEBUG("**** CHotSpotServer::NewSessionL");
+	CHotSpotSession* session = 
+        CHotSpotSession::NewL( const_cast<CHotSpotServer&>( *this ) );
+    return session;
+    }
+
+// ---------------------------------------------------------
+// NotifyAdd
+// ---------------------------------------------------------
+//
+void CHotSpotServer::NotifyAdd(
+    CNotificationBase& aNotification )
+    {
+    DEBUG( "CHotSpotServer::NotifyAdd()" );
+    iNotificationArray.Insert( &aNotification, 0 );
+    }
+
+// ---------------------------------------------------------
+// NotifyRemove
+// ---------------------------------------------------------
+//
+void CHotSpotServer::NotifyRemove(
+    CNotificationBase& aNotification )
+    {
+    DEBUG( "CHotSpotServer::NotifyRemove()" );
+    TInt index = iNotificationArray.Find( &aNotification );
+    iNotificationArray.Remove( index );
+    }
+
+// -----------------------------------------------------------------------------
+// SaveMessage
+// -----------------------------------------------------------------------------
+//
+TInt CHotSpotServer::SaveMessage( TUint aIapId, const RMessage2& aMessage, 
+                                  THotSpotCommands aCommand )
+    {
+    DEBUG("**** HotspotServer: SaveMessage");
+    TInt ret( KErrNone );
+    SRequestMapEntry entry;
+    entry.iMessage = aMessage;
+    entry.iFunction = aCommand;
+    entry.iIapId = aIapId;
+    ret = iMap.Append(entry);
+    return ret;
+    }
+
+// -----------------------------------------------------------------------------
+// CompleteMessage
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::CompleteMessage( TInt aIndex, TInt aResult )
+    {
+    DEBUG("**** HotspotServer: CompleteMessage");
+    SRequestMapEntry entry = iMap[aIndex];
+    
+    RMessagePtr2 message = entry.iMessage;
+    message.Complete( aResult );
+    iMap.Remove( aIndex );
+    }
+
+// -----------------------------------------------------------------------------
+// EditMessage
+// -----------------------------------------------------------------------------
+//
+TInt CHotSpotServer::EditMessage( TInt aIndex, TPckg<HssScanList>& aData)
+    {
+    DEBUG("**** HotspotServer: EditMessage");
+    TInt ret( KErrNone );
+    
+    iMap[aIndex].iMessage.Write(0, aData);
+    
+    DEBUG1("**** HotspotServer: EditMessage ret: %d", ret);
+    return ret;
+    }
+
+
+// -----------------------------------------------------------------------------
+// FindMessage
+// -----------------------------------------------------------------------------
+//
+TInt CHotSpotServer::FindMessage( TUint aIapId, THotSpotCommands aCommand )
+    {
+    DEBUG("**** HotspotServer: FindMessage");
+    TInt ret( KErrNotFound );
+    SRequestMapEntry entry;
+    
+    for ( TInt i = 0; i < iMap.Count(); i++ ) 
+        {
+        entry = iMap[i];
+        if ( ( entry.iFunction == aCommand ) && ( entry.iIapId == aIapId ) )
+            {
+            // Correct message found
+            // Now stop loop and return index value, so that CompeteMessage can
+            // be called with that
+            ret = i;
+            i = iMap.Count();
+            }
+        }
+    DEBUG1("**** HotspotServer: FindMessage ret: %d", ret);
+    return ret;
+    }
+
+// -----------------------------------------------------------------------------
+// SetLogoutFlag
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::SetLogoutFlag( TBool aValue )
+    {
+    DEBUG("HotspotServer::SetLogoutFlag");
+    iLogoutSent = aValue;
+    }
+    
+// -----------------------------------------------------------------------------
+// GetLogoutFlagValue
+// -----------------------------------------------------------------------------
+//
+TBool CHotSpotServer::GetLogoutFlagValue()
+    {
+    DEBUG("HotspotServer::GetLogoutFlagValue()");
+    return iLogoutSent;
+    }
+
+// -----------------------------------------------------------------------------
+// SetLoginFlag
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::SetLoginFlag( TBool aValue )
+    {
+    DEBUG("HotspotServer::SetLoginFlag");
+    iLoginValue = aValue;
+    }
+    
+// -----------------------------------------------------------------------------
+// GetLoginFlagValue
+// -----------------------------------------------------------------------------
+//
+TBool CHotSpotServer::GetLoginFlagValue()
+    {
+    DEBUG("HotspotServer::GetLoginFlagValue()");
+    return iLoginValue;
+    }
+
+
+// -----------------------------------------------------------------------------
+// SetAssociationFlag
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::SetAssociationFlag( TBool aValue )
+    {
+    DEBUG("HotspotServer::SetAssociationFlag");
+    iAssociationValue = aValue;
+    }
+        
+// -----------------------------------------------------------------------------
+// GetAssociationFlagValue
+// -----------------------------------------------------------------------------
+//
+TBool CHotSpotServer::GetAssociationFlagValue()
+    {
+    DEBUG("HotspotServer::GetAssociationFlagValue()");
+    return iAssociationValue;
+    }
+
+// -----------------------------------------------------------------------------
+// SetServiceId
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::SetServiceId( TInt aServiceId )
+    {
+    DEBUG("HotspotServer::SetServiceId");
+    iCurrentServiceIdInUse = aServiceId;
+    }
+    
+// -----------------------------------------------------------------------------
+// GetServiceId
+// -----------------------------------------------------------------------------
+//
+TInt CHotSpotServer::GetServiceId()
+    {
+    DEBUG("HotspotServer::GetServiceId()");
+    return iCurrentServiceIdInUse;
+    }
+    
+// -----------------------------------------------------------------------------
+// RunError
+// -----------------------------------------------------------------------------
+//
+TInt CHotSpotServer::RunError( TInt aError )
+	{
+    // error from CHotSpotSession::ServiceL
+    Message().Complete( aError );
+    // Continue  reading client requests
+    ReStart();
+    return (KErrNone);
+	}
+
+// -----------------------------------------------------------------------------
+// EasyWlanIdL
+// -----------------------------------------------------------------------------
+//
+TBool CHotSpotServer::EasyWlanIdL()
+    {
+    TBool ret( EFalse );
+    DEBUG("CHotSpotServer::EasyWlanIdL()");
+    RCmManagerExt cmManager;
+    cmManager.OpenL();
+    CleanupClosePushL( cmManager );
+    
+    iEasyWlanId = cmManager.EasyWlanIdL();
+    DEBUG1("CHotSpotServer::EasyWlanIdL() ret: % d", iEasyWlanId);
+    // if iEasyWlanId is 0, then it was not found
+    if ( iEasyWlanId > 0 )
+        {
+        RCmConnectionMethodExt plugin = cmManager.ConnectionMethodL( iEasyWlanId );
+        CleanupClosePushL( plugin );
+        //iEasyWlanId = plugin.GetIntAttributeL( /*ECmIapServiceId*/EWlanServiceId )
+        plugin.SetStringAttributeL( ECmConfigDaemonManagerName, KHotSpotPlugin );
+        // commit changes
+        plugin.UpdateL();
+        CleanupStack::PopAndDestroy( &plugin ); // Close() called on "plugin"
+        ret = ETrue;
+        }
+    CleanupStack::PopAndDestroy( &cmManager );
+    DEBUG("CHotSpotServer::EasyWlanIdL() DONE");
+    return ret;
+    }
+
+// -----------------------------------------------------------------------------
+// GetEasyWlanId
+// -----------------------------------------------------------------------------
+//
+TUint32 CHotSpotServer::GetEasyWlanId()
+    {
+    return iEasyWlanId;
+    }
+
+// -----------------------------------------------------------------------------
+// GetLoginTimerMicroSecs
+// -----------------------------------------------------------------------------
+//
+TUint CHotSpotServer::GetLoginTimeMicroSecs( TUid aClientUid )
+    {
+    // Default timer value will be returned if matching client UID isn't found.
+    TUint retval = KHssDefaultLoginTimeMicroSecs ;
+    
+    TInt ret = CHotSpotServer::FindClientUid( aClientUid );
+    if (ret != KErrNotFound)
+        {
+        // ret is the matching element's index.
+        retval = iLoginLogoutTimerArray[ret].loginTimeMicroSecs;
+        }
+
+    return retval;
+    }
+
+// -----------------------------------------------------------------------------
+// GetLogoutTimerMicroSecs
+// -----------------------------------------------------------------------------
+//
+TUint CHotSpotServer::GetLogoutTimeMicroSecs( TUid aClientUid )
+    {
+    // Default timer value will be returned if matching client UID isn't found.
+    TUint retval = KHssDefaultLogoutTimeMicroSecs ;
+    
+    TInt ret = CHotSpotServer::FindClientUid( aClientUid );
+    if (ret != KErrNotFound)
+        {
+        // ret is the matching element's index.
+        retval = iLoginLogoutTimerArray[ret].logoutTimeMicroSecs;
+        }
+
+    return retval;
+    }
+
+// -----------------------------------------------------------------------------
+// SetTimerValues
+// -----------------------------------------------------------------------------
+//
+void CHotSpotServer::SetTimerValues(
+        TUid aClientUid,
+        TUint aLoginTimerValue,   // in seconds
+        TUint aLogoutTimerValue ) // in seconds
+    {    
+    TInt ret = CHotSpotServer::FindClientUid( aClientUid );
+    TUint loginTimeMicroSecs = KHssDefaultLoginTimeMicroSecs ;
+    TUint logoutTimeMicroSecs = KHssDefaultLogoutTimeMicroSecs ;
+    
+    // Check that values are in bounds and modify them into micro seconds.
+    if( aLoginTimerValue >= KHssMinLoginTime &&
+            aLoginTimerValue <= KHssMaxLoginTime )
+        {
+        loginTimeMicroSecs = KMillion * aLoginTimerValue;
+        }
+
+    if( aLogoutTimerValue >= KHssMinLogoutTime &&
+            aLogoutTimerValue <= KHssMaxLogoutTime )
+        {
+        logoutTimeMicroSecs = KMillion * aLogoutTimerValue;
+        }
+
+    if (ret != KErrNotFound)
+        {
+        DEBUG("CHotSpotServer::SetTimerValues(): Existing client modified.");
+        // ret is the matching element's index.
+        iLoginLogoutTimerArray[ret].loginTimeMicroSecs = loginTimeMicroSecs;
+        iLoginLogoutTimerArray[ret].logoutTimeMicroSecs = logoutTimeMicroSecs;
+        }
+    else
+        {
+        DEBUG("CHotSpotServer::SetTimerValues(): New Client added.");
+        // Create a new element and append it to the array.
+        const SLoginLogoutTimers addedElement = {
+                aClientUid,
+                loginTimeMicroSecs,
+                logoutTimeMicroSecs };
+        iLoginLogoutTimerArray.Append( addedElement );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// FindClientUid
+// -----------------------------------------------------------------------------
+//
+TInt CHotSpotServer::FindClientUid( TUid aClientUid )
+    {
+    TInt count = iLoginLogoutTimerArray.Count();
+    TInt i = 0;
+    TInt ret = KErrNotFound;
+    
+    while (i < count)
+        {
+        if (aClientUid == iLoginLogoutTimerArray[i].clientUid)
+            {
+            ret = i;
+            break;
+            }
+        i++;
+        }
+
+    return ret;    
+    }
+
+// ========================== OTHER EXPORTED FUNCTIONS =========================
+
+// -----------------------------------------------------------------------------
+// E32Main implements the executable entry function.
+// Note that because the target type of the IPWServer Hss
+// is EXEDLL, the entry point has different signature depending
+// on the build platform.
+// Creates a cleanup stack and runs the server. 
+// Returns: Zero
+// -----------------------------------------------------------------------------
+//
+GLDEF_C TInt E32Main()
+    {
+    __UHEAP_MARK;
+    DEBUG("**** HotspotServer: E32Main");
+    CTrapCleanup* cleanup=CTrapCleanup::New();
+    TInt r=KErrNoMemory;
+    if (cleanup)
+        {
+        TRAP(r,RunServerL());
+        delete cleanup;
+        }
+    else
+        {
+        User::Panic( KHotSpotModuleName, KHotSpotPanicCleanupStackCreationFailed );
+        }
+    __UHEAP_MARKEND;
+    return r;
+    }
+ 
+// end of file