presetserver/serversrc/Psserver.cpp
changeset 0 09774dfdd46b
child 12 608f67c22514
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/presetserver/serversrc/Psserver.cpp	Mon Apr 19 14:01:53 2010 +0300
@@ -0,0 +1,713 @@
+/*
+* Copyright (c) 2006-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:  Preset server implementation
+*
+*/
+
+
+#include <pscommon.h>
+#include <sbdefs.h>
+
+#include "psdatabasecleanup.h"
+#include "psdebug.h"
+#include "pspendingpreset.h"
+#include "psscheduler.h"
+#include "psserver.h"
+#include "pssession.h"
+#include "psshutdown.h"
+#include "psutils.h"
+
+using namespace conn;
+
+const TInt KPSPriority = CActive::EPriorityStandard;    // Priority of the preset server.
+
+const TInt KPSShutdownTime = 500000; // The time in microseconds between the last client disconnecting and the server being closed.
+const TInt KPSCachedNotificationCleanerPeriod = 60000000; // The period in microseconds between checking for unused cached notifications.
+const TInt KPSCachedNotificationKeepAliveTime = 300000000; // The amount of time in microseconds to keep cached notifications alive after they have been added.
+
+// ======== LOCAL FUNCTIONS ========
+
+namespace
+    {
+
+#ifdef _DEBUG
+
+    // ---------------------------------------------------------------------------
+    // Panics the process.
+    // ---------------------------------------------------------------------------
+    //
+    void Panic( TInt aReason )
+        {
+        User::Panic( KPSServerName, aReason );
+        }
+
+#endif // _DEBUG
+
+    // ---------------------------------------------------------------------------
+    // Sorts the array by priority of the notification observers. Descending order!
+    // ---------------------------------------------------------------------------
+    //
+    TInt SortPresetObserverArrayByPriority( const RMessage2& aMessage1, const RMessage2& aMessage2 )
+        {
+        if ( aMessage1.Int1() < aMessage2.Int1() )
+            {
+            return 1;
+            }
+        else if ( aMessage1.Int1() > aMessage2.Int1() )
+            {
+            return -1;
+            }
+        else
+            {
+            return 0;
+            }
+        }
+
+    // ---------------------------------------------------------------------------
+    // Initializes the server process.
+    // ---------------------------------------------------------------------------
+    //
+    void InitServerL()
+        {
+        User::RenameThread( KPSServerName );
+        
+        TFindServer findServer( KPSServerName );
+        TFullName fullName;
+        
+        if ( findServer.Next( fullName ) ) // Server has not yet been started.
+            {
+            CPSScheduler* scheduler = new ( ELeave ) CPSScheduler;
+            CleanupStack::PushL( scheduler );
+            CActiveScheduler::Install( scheduler );
+
+            CPSServer* server = CPSServer::NewL();
+            CleanupStack::Pop( scheduler );
+            scheduler->SetServer( server );
+
+            // Attention! Absolutely no code that can leave may be run after doing the rendezvous!
+
+            RProcess().Rendezvous( KErrNone );        
+
+            CActiveScheduler::Start();
+            
+            delete server;
+            delete scheduler;
+            }
+        else
+            {
+            RProcess().Rendezvous( KErrNone );
+            }
+        }
+
+    }
+    
+// ======== MEMBER FUNCTIONS ========
+
+// ---------------------------------------------------------------------------
+// Constructor.
+// ---------------------------------------------------------------------------
+//
+CPSServer::CPSServer()
+    : CServer2( KPSPriority ), iDatabase( *this ), iShutdownDelay( KPSShutdownTime )
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// Second-phase constructor.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::ConstructL()
+    {
+    PSDEBUG( "CPSServer::ConstructL() [enter]" );
+
+    User::LeaveIfError( iFs.Connect() );
+
+    TFileName databaseFullName;
+    PSUtils::GetDatabaseFullNameL( databaseFullName );
+
+    iDatabase.OpenL( iFs, databaseFullName );
+
+    iShutdown = CPSShutdown::NewL();
+    iCleanup = CPSDatabaseCleanup::NewL( iDatabase );
+
+    iCachedNotificationCleaner = CPeriodic::NewL( CActive::EPriorityStandard );
+    iCachedNotificationCleaner->Start( KPSCachedNotificationCleanerPeriod, KPSCachedNotificationCleanerPeriod,
+                                       TCallBack( StaticCleanCachedNotifications, this ) ); 
+    
+    iPropertyObserver = CPSPropertyObserver::NewL( *this, KUidSystemCategory, 
+                                                   KUidBackupRestoreKey, RProperty::EInt);
+    
+    StartL( KPSServerName );
+
+    PSDEBUG( "CPSServer::ConstructL() [exit]" );
+    }
+
+// ---------------------------------------------------------------------------
+// Two-phased constructor.
+// ---------------------------------------------------------------------------
+//
+CPSServer* CPSServer::NewL()
+    {
+    CPSServer* self = new ( ELeave ) CPSServer;
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// Destructor.
+// ---------------------------------------------------------------------------
+//
+CPSServer::~CPSServer()
+    {
+    PSDEBUG( "CPSServer::~CPSServer() [enter]" );
+
+    Cancel();
+    
+    delete iPropertyObserver;
+    delete iCachedNotificationCleaner;
+    delete iShutdown;
+    delete iCleanup;
+
+    iDatabase.Close();
+    iFs.Close();
+
+    while ( iPresetObservers.Count() )
+        {
+        if ( !iPresetObservers[0].IsNull() )
+            {
+            iPresetObservers[0].Complete( KErrServerTerminated );
+            }
+        iPresetObservers.Remove( 0 );
+        }
+    iPresetObservers.Close();
+
+    iNotifications.Close();
+    iPendingPresets.ResetAndDestroy();
+
+    PSDEBUG( "CPSServer::~CPSServer() [exit]" );
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the current message.
+// ---------------------------------------------------------------------------
+//
+const RMessage2& CPSServer::Message() const
+    {
+    return CServer2::Message();
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the database.
+// ---------------------------------------------------------------------------
+//
+RPSDatabase& CPSServer::Database()
+    {
+    return iDatabase;
+    }
+
+// ---------------------------------------------------------------------------
+// Panics the client.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::PanicClient( TInt aReason ) const
+    {
+    Message().Panic( KPSServerName, aReason );
+    }
+
+// ---------------------------------------------------------------------------
+// Increments the session count.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::IncrementSessionCount()
+    {
+    iSessionCount++;
+    
+    iShutdown->Cancel();
+    }
+
+// ---------------------------------------------------------------------------
+// Decrements the session count.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::DecrementSessionCount()
+    {
+    iSessionCount--;
+    
+    if ( iSessionCount == 0 ) // Last session was closed, so we need to start the shutdown timer.
+        {
+        iShutdown->Start( iShutdownDelay );
+        }
+
+    __ASSERT_DEBUG( iSessionCount >= 0, Panic( KErrCorrupt ) );
+    }
+
+// ---------------------------------------------------------------------------
+// Called when a session is disconnected.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::SessionDisconnected( const RMessage2& aMessage )
+    {
+    PSDEBUG( "CPSServer::SessionDisconnected( RMessage2& ) [enter]" );
+
+    for ( TInt i = 0; i < iPresetObservers.Count(); i++ )
+        {
+        const RMessage2& message = iPresetObservers[i];
+        
+        if ( message.Session() == aMessage.Session() )
+            {
+            if ( !message.IsNull() )
+                {
+                message.Complete( KErrDied );
+                }
+            iPresetObservers.Remove( i );
+            i--;
+            }
+        }
+
+    PSDEBUG( "CPSServer::SessionDisconnected( RMessage2& ) [exit]" );
+    }
+
+// ---------------------------------------------------------------------------
+// Appends a preset observer.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::AppendPresetObserverL( const RMessage2& aMessage )
+    {
+    PSDEBUG( "CPSServer::AppendPresetObserverL( RMessage2& ) [enter]" );
+
+    TInt idx = PresetObserverIndex( aMessage );
+    
+    if ( idx != KErrNotFound )
+        {
+        PSDEBUG( "CPSServer::AppendPresetObserverL - Preset observer already found" );
+        User::Leave( KErrAlreadyExists );
+        }
+
+    TPckgBuf<TPSNotifyDataPackage> data;
+    aMessage.ReadL( 2, data );
+    
+    TInt nextUnhandledNotificationIndex = NextUnhandledNotificationIndexById( data().iNotificationId, aMessage.Int1() );
+    
+    if ( data().iNotificationId > 0 && nextUnhandledNotificationIndex >= 0 )
+        {
+        PSDEBUG2( "CPSServer::AppendPresetObserverL - Cached notification that was missed was found at index %d", nextUnhandledNotificationIndex );
+
+        aMessage.WriteL( 2, TPckgC<TPSNotifyDataPackage>( iNotifications[nextUnhandledNotificationIndex] ) );
+        aMessage.Complete( KErrNone ); 
+        }
+    else
+        {
+        PSDEBUG( "CPSServer::AppendPresetObserverL - No cached notifications of interest were found, starting to observe further ones" );
+        iPresetObservers.AppendL( RMessage2( aMessage ) );
+        }
+
+    PSDEBUG( "CPSServer::AppendPresetObserverL( RMessage2& ) [enter]" );
+    }
+
+// ---------------------------------------------------------------------------
+// Removes a preset observer and completes it with KErrCancel.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::RemovePresetObserver( const RMessage2& aMessage )
+    {
+    TInt idx = PresetObserverIndex( aMessage );
+
+    if ( idx >= 0 )
+        {
+        if ( !iPresetObservers[idx].IsNull() )
+            {
+            iPresetObservers[idx].Complete( KErrCancel );
+            }
+        iPresetObservers.Remove( idx );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the index of a given preset observer.
+// ---------------------------------------------------------------------------
+//
+TInt CPSServer::PresetObserverIndex( const RMessage2& aMessage ) const
+    {
+    TInt idx = KErrNotFound;
+        
+    CSession2* currentSession = aMessage.Session();
+        
+    for ( TInt i = 0; i < iPresetObservers.Count(); i++ )
+        {
+        const RMessage2& message = iPresetObservers[i];
+
+        if ( !message.IsNull() && message.Session() == currentSession && message.Int0() == aMessage.Int0() )
+            {
+            idx = i;
+            /*lint -save -e960 (Note -- Violates MISRA Required Rule 58, non-switch break used)*/
+            break;
+            /*lint -restore*/
+            }
+        }
+    
+    return idx;
+    }
+
+// ---------------------------------------------------------------------------
+// Appends a pending preset.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::AppendPendingPresetL( TInt aId )
+    {
+    TInt idx = PendingPresetIndex( aId );
+
+    if ( idx == KErrNotFound )
+        {
+        CPSPendingPreset* preset = CPSPendingPreset::NewL( aId );
+        CleanupStack::PushL( preset );
+        iPendingPresets.AppendL( preset );
+        CleanupStack::Pop( preset );
+        }
+    else
+        {
+        User::Leave( KErrLocked );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Commits a pending preset.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::CommitPendingPresetL( TInt aId )
+    {
+    TInt idx = PendingPresetIndex( aId );
+
+    if ( idx >= 0 )
+        {
+        CPSPendingPreset* preset = iPendingPresets[idx];
+        iDatabase.CommitPresetL( *preset );
+        iPendingPresets.Remove( idx );
+        delete preset;
+        }
+    else
+        {
+        User::Leave( KErrNotReady );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Removes a pending preset from the array.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::RemovePendingPreset( TInt aId )
+    {
+    TInt idx = PendingPresetIndex( aId );
+    
+    if ( idx >= 0 )
+        {
+        CPSPendingPreset* preset = iPendingPresets[idx];
+        iPendingPresets.Remove( idx );
+        delete preset;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Sets the index of a pending preset.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::SetPendingPresetIndexL( TInt aId, TInt aIndex )
+    {
+    TInt idx = PendingPresetIndex( aId );
+    
+    if ( idx >= 0 )
+        {
+        iPendingPresets[idx]->SetIndex( aIndex ); 
+        }
+    else
+        {
+        User::Leave( KErrNotReady );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Sets the name of a pending preset.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::SetPendingPresetNameL( TInt aId, const TDesC& aName )
+    {
+    TInt idx = PendingPresetIndex( aId );
+    
+    if ( idx >= 0 )
+        {
+        iPendingPresets[idx]->SetNameL( aName ); 
+        }
+    else
+        {
+        User::Leave( KErrNotReady );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Sets the data of a pending preset.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::SetPendingPresetDataL( TInt aId, const TDesC8& aData )
+    {
+    TInt idx = PendingPresetIndex( aId );
+    
+    if ( idx >= 0 )
+        {
+        iPendingPresets[idx]->SetDataL( aData ); 
+        }
+    else
+        {
+        User::Leave( KErrNotReady );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// From class CSession2
+// Creates a new session.
+// ---------------------------------------------------------------------------
+//
+CSession2* CPSServer::NewSessionL( const TVersion& aVersion, const RMessage2& /*aMessage*/ ) const
+    {
+    TVersion currentVersion( KPSVersionMajor, KPSVersionMinor, KPSVersionBuild );
+    
+    if ( !User::QueryVersionSupported( currentVersion, aVersion ) )
+        {
+        User::Leave( KErrNotSupported );
+        }
+
+    return CPSSession::NewL( *const_cast<CPSServer*>( this ) );
+    }
+
+// ---------------------------------------------------------------------------
+// From class MPSPresetObserver
+// Invoked when a preset is changed.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::HandlePresetChangedL( TInt aId, TUid aDataHandler, MPSPresetObserver::TPSReason aReason )
+    {
+    NotifyPresetObserversL( aId, aDataHandler, aReason );
+    
+    if (aReason == MPSPresetObserver::EPSDeleted)
+    	{
+    	iCleanup->RequestCleanupCheck();
+    	}
+    }
+
+// ---------------------------------------------------------------------------
+// Returns the index of a pending preset matching the supplied id.
+// ---------------------------------------------------------------------------
+//
+TInt CPSServer::PendingPresetIndex( TInt aId ) const
+    {
+    TInt idx = KErrNotFound;
+
+    for ( TInt i = 0; i < iPendingPresets.Count(); i++ )
+        {
+        if ( iPendingPresets[i]->Id() == aId )
+            {
+            idx = i;
+            /*lint -save -e960 (Note -- Violates MISRA Required Rule 58, non-switch break used)*/
+            break;
+            /*lint -restore*/
+            }
+        }
+
+    return idx;
+    }
+
+// ---------------------------------------------------------------------------
+// Notifies the preset observers.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::NotifyPresetObserversL( TInt aId, TUid aDataHandler, MPSPresetObserver::TPSReason aReason )
+    {
+    PSDEBUG5( "CPSServer::NotifyPresetObserversL( aId = %d, aDataHandler = %d, aReason = %d ) - Notifications cached: %d", aId, aDataHandler.iUid, aReason, iNotifications.Count() );
+
+    TTime current;
+    current.UniversalTime();
+
+    iCachedNotificationId++; // Running cached notification identifier is incremented here. Must be done before the data package is constructed.
+    
+    TPSNotifyDataPackage data = { aId, aDataHandler, aReason, iCachedNotificationId, current };
+    iNotifications.AppendL( data );
+
+    // Sorts the preset observers array so, that presets are first, then other observers.
+    // This is done to ensure that presets are notified first, just in case.
+    iPresetObservers.Sort( TLinearOrder<RMessage2>( ::SortPresetObserverArrayByPriority ) );
+
+    TInt skipped = 0; // Number of observers that will not be notified, i.e. are skipped.
+
+    while ( iPresetObservers.Count() && skipped < iPresetObservers.Count() )
+        {
+        const RMessage2& message = iPresetObservers[skipped];
+
+        if ( message.IsNull() )
+            {
+            iPresetObservers.Remove( skipped ); // Dead notifiers are removed from the array.
+            }
+        else
+            {
+            TPckgBuf<TPSNotifyDataPackage> msgData;
+            message.ReadL( 2, msgData );
+
+            TInt observedPresetId = message.Int1();
+
+            // If the observer is interested in this preset or it is interested in them all (KErrNotFound),
+            // notify it about the change.
+            if ( aId == observedPresetId || observedPresetId == KErrNotFound )
+                {
+                message.WriteL( 2, TPckgC<TPSNotifyDataPackage>( data ) );
+                message.Complete( KErrNone );
+                
+                iPresetObservers.Remove( skipped );
+                }
+            else
+                {
+                skipped++;
+                }
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Cleanup routine for deleting unused cached notifications.
+// ---------------------------------------------------------------------------
+//
+TInt CPSServer::NextUnhandledNotificationIndexById( TUint64 aHandledNotificationId, TInt aObservedPresetId )
+    {
+    TInt index = KErrNotFound;
+    
+    for ( TInt i = 0; i < iNotifications.Count() && index == KErrNotFound; i++ )
+        {
+        if ( iNotifications[i].iNotificationId > aHandledNotificationId )
+            {
+            if ( iNotifications[i].iId == aObservedPresetId || aObservedPresetId == KErrNotFound )
+                {
+                index = i;
+                }
+            }
+        }
+    
+    return index;
+    }
+
+// ---------------------------------------------------------------------------
+// Cleanup routine for deleting unused cached notifications.
+// ---------------------------------------------------------------------------
+//
+TInt CPSServer::StaticCleanCachedNotifications( TAny* aSelf )
+    {
+    CPSServer* self = static_cast<CPSServer*>( aSelf );
+    if ( self )
+        {
+        PSDEBUG2( "CPSServer::StaticCleanCachedNotifications [enter] - iNotifications.Count() = %d", self->iNotifications.Count() );
+
+        TTime current;
+        current.UniversalTime();
+        
+        for ( TInt i = 0; i < self->iNotifications.Count(); i++ )
+            {
+            TInt64 diff = current.MicroSecondsFrom( self->iNotifications[i].iNotificationTime ).Int64();
+
+            if ( diff > KPSCachedNotificationKeepAliveTime )
+                {
+                PSDEBUG2( "CPSServer::StaticCleanCachedNotifications - Removing cached notification entry from index with time difference of %d seconds", diff / 1000000 ); 
+                self->iNotifications.Remove( i );
+                i--;
+                }
+            }
+        
+        self->iNotifications.Compress();
+
+        PSDEBUG2( "CPSServer::StaticCleanCachedNotifications [exit] - iNotifications.Count() = %d", self->iNotifications.Count() );
+        }
+
+    return KErrNone;
+    }
+    
+// ---------------------------------------------------------------------------
+// From class MPSPropertyChangeObserver
+// Invoked when a TInt type property is changed.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::HandlePropertyChangeL( const TUid& /* aCategory */, const TUint /* aKey */, const TInt aValue )
+    {
+    PSDEBUG2( "CPSServer::HandlePropertyChangeL( aValue = %d )", aValue);
+    TUint backupPart = aValue & KBURPartTypeMask; 
+    if ( backupPart != EBURUnset && backupPart != EBURNormal ) // backup or restore -> shutdown when possible.
+        {
+        iShutdownDelay = 0;        
+        if ( iSessionCount == 0 )
+            {
+            iShutdown->Start( iShutdownDelay );
+            }
+        }
+    else
+        {
+        PSDEBUG("CPSServer::HandlePropertyChangeL - no backup/restore ongoing" );
+        }        
+    }
+
+// ---------------------------------------------------------------------------
+// From class MPSPropertyChangeObserver
+// Invoked when a TBuf8 type property is changed.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::HandlePropertyChangeL( const TUid& /* aCategory */, 
+                                       const TUint /*aKey*/, 
+                                       const TPSTextProperty& /*aValue */ ){}
+
+// ---------------------------------------------------------------------------
+// From class MPSPropertyChangeObserver
+// Invoked when an error is encountered in property processing.
+// ---------------------------------------------------------------------------
+//
+void CPSServer::HandlePropertyChangeErrorL( const TUid& /* aCategory */, 
+                                            const TUint /* aKey */, 
+                                            TInt /*aError */){}
+    
+    
+
+// ======== GLOBAL FUNCTIONS ========
+
+// ---------------------------------------------------------------------------
+// Application entry point.
+// ---------------------------------------------------------------------------
+//
+TInt E32Main()
+    {
+    __UHEAP_MARK;
+
+    TInt err = KErrNone;
+
+    CTrapCleanup* cleanupStack = CTrapCleanup::New();
+    if ( cleanupStack )
+        {
+        TRAP( err, InitServerL() )
+
+        __ASSERT_DEBUG( !err, Panic( err ) );
+        PSDEBUG( "CPSServer - Exiting" );
+        delete cleanupStack;        
+        }
+    else
+        {
+        err = KErrNoMemory;
+        }
+        
+    __UHEAP_MARKEND;
+    
+    return err;
+    }
+