upnp/upnpstack/upnputils/src/upnpdevicelibrary.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:27:00 +0100
branchRCL_3
changeset 10 594d15129e2c
parent 9 5c72fd91570d
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201029 Kit: 201035

/** @file
* Copyright (c) 2005-2008 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:  CUpnpDeviceLibrary
*
*/


// INCLUDE FILES
#include "upnpdevicelibrary.h"
#include "upnpssdpmessage.h"
#define KLogFile _L("DLNAWebServer.txt")
#include "upnpcustomlog.h"

static const TInt KIntervalBetweenByeAndAlive = 1;
static const TInt KIntervalBeforeFirstAlive = 1;
// prevents treating SSDP alive messages sent by local device
// as an external device messages in case of quick local device restarting
// heuristic value 
static const TInt KLocalDeviceHysteresisWindow  = 20;
// used for IPC communication 2 phase request
// heuristic value 
static const TInt KIpcCommunicationTimeout = 2;

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::CUpnpDeviceLibrary
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CUpnpDeviceLibrary::CUpnpDeviceLibrary( MUpnpDeviceLibraryObserver& aObserver,
                                TInt aHandle )
    : iObserver(aObserver),
      iHandle( aHandle )
    {
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CUpnpDeviceLibrary::ConstructL()
    {
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpDeviceLibrary* CUpnpDeviceLibrary::NewL( 
                                        MUpnpDeviceLibraryObserver& aObserver,
                                        TInt aHandle )
    {
    CUpnpDeviceLibrary* self = new (ELeave) CUpnpDeviceLibrary( aObserver, aHandle );

    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    
    return self;
    }
// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::~CUpnpDeviceLibrary
// Destructor
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpDeviceLibrary::~CUpnpDeviceLibrary()
    {
    LOGSH( iHandle, "Devicelibrary ~CUpnpDeviceLibrary");

    iElementArray.ResetAndDestroy();
    iElementArray.Close();
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::PrepareShutdown
// Prepare devices for shutdown.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::PrepareShutdown()
    {
    LOGSH( iHandle, "Devicelibrary PrepareShutdown" );

    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::AddInfoL
// Add device info.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::AddInfoL( const TUpnpAddLocalDevice* aIndex, 
                                        const TDesC8& aBuffer, 
                                        const TInetAddr& aLocalAddr )
    {
    LOGSH( iHandle, "Devicelibrary AddInfoL" );
    if ( !aIndex )
        {
        return;
        }
    
    TPtrC8 uuid = aBuffer.Left(aIndex->iUuidLength);    
    TInt index = Find( uuid );
    
    if ( index != KErrNotFound )
        {        
        CUpnpDeviceLibraryElement* elem = iElementArray[index];
        
        if ( elem->Local() )
            {            
            CUpnpTimeoutElement::TRenew state = elem->Renew();          
            elem->AddInfoL( aIndex, aBuffer, aLocalAddr );                        
            elem->SetRenew( CUpnpTimeoutElement::ERenew );
            elem->SetTimeout( KIntervalBeforeFirstAlive );        
            elem->SetUpdateId( iUpdateId );
                 
            if ( state == CUpnpTimeoutElement::ERemove ) 
                {   
                elem->SetAlive( ETrue );                
                elem->SetUpdateId( ++iUpdateId );
                iObserver.DeviceListChangedL();
                }        
            }
        else
            {
            InvalidateNonLocalDevice( *elem );
            AppendLocalDeviceL( aIndex, aBuffer, aLocalAddr );                            
            iObserver.DeviceListChangedL();            
            }                
        }
    else
        {        
        AppendLocalDeviceL( aIndex, aBuffer, aLocalAddr );                
        iObserver.DeviceListChangedL();
        }
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::InvalidateNonLocalDevice
// -----------------------------------------------------------------------------
//
void CUpnpDeviceLibrary::InvalidateNonLocalDevice( CUpnpDeviceLibraryElement& aElem )
    {
    aElem.SetAlive( EFalse );
    aElem.SetUpdateId( ++iUpdateId );
    aElem.SetTimeout( KIpcCommunicationTimeout );                            
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::AppendLocalDeviceL
// -----------------------------------------------------------------------------
//
void CUpnpDeviceLibrary::AppendLocalDeviceL( const TUpnpAddLocalDevice* aIndex, 
                         const TDesC8& aBuffer, 
                         const TInetAddr& aLocalAddr )
    {    
    CUpnpDeviceLibraryElement* element = CUpnpDeviceLibraryElement::NewL(*this);
    CleanupStack::PushL( element );
    element->AddInfoL( aIndex, aBuffer, aLocalAddr );
    element->SetRenew( CUpnpTimeoutElement::ERenew );
    element->SetTimeout( KIntervalBeforeFirstAlive );
    element->SetLocal( ETrue );
    element->SetUpdateId( ++iUpdateId );
    iElementArray.AppendL( element );
    CleanupStack::Pop( element );    
    }
// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::AddInfoL
// Add device info.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::AddInfoL( const TUpnpAddLocalDevice* aIndex, 
                                        const TDesC8& aBuffer )
    {
    LOGSH( iHandle, "Devicelibrary AddInfoL");

    if ( !aIndex )
        {
        return;
        }
    
    TPtrC8 uuid = aBuffer.Left(aIndex->iUuidLength);
    
    TInt index = Find( uuid );
    
    if ( index != KErrNotFound )
        {
        CUpnpDeviceLibraryElement* elem = iElementArray[index];
        
        CUpnpTimeoutElement::TRenew state = elem->Renew();
        
        elem->AddInfoL( aIndex, aBuffer );                        
        elem->SetRenew( CUpnpTimeoutElement::ERenew );
        elem->SetTimeout( 1 );        
        elem->SetUpdateId( iUpdateId );
             
        if (state == CUpnpTimeoutElement::ERemove) 
            {   
            iUpdateId++; 
            elem->SetAlive( ETrue );                
            elem->SetUpdateId( iUpdateId );
            iObserver.DeviceListChangedL();
            }        
        }
    else
        {
        iUpdateId++;
        
        CUpnpDeviceLibraryElement* element = CUpnpDeviceLibraryElement::NewL(*this);
        CleanupStack::PushL( element );
        element->AddInfoL( aIndex, aBuffer );
        element->SetRenew( CUpnpTimeoutElement::ERenew );
        element->SetTimeout( 1 );
        element->SetLocal( EFalse );
        element->SetUpdateId( iUpdateId );
        iElementArray.AppendL( element );
        CleanupStack::Pop( element );
        
        iObserver.DeviceListChangedL();
        }
    }
// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::AddInfoL
// Add device info.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::AddInfoL( CUpnpSsdpMessage* aMessage )
    {
    LOGSH( iHandle, "Devicelibrary AddInfoL");
    if ( aMessage )
        {
        if ( aMessage->IsSsdpAlive() )
            {       
            if( AddDeviceL(aMessage) )
                {  
                iObserver.DeviceListChangedL();
                }
            }
        else
            {
            RemoveDeviceL( aMessage );
            iObserver.DeviceListChangedL();
            }
        }
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::Find
// Find device given by UUID.
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CUpnpDeviceLibrary::Find( const TDesC8& aUuid )
    {
    LOGSH( iHandle, "Devicelibrary Find");

    for ( TInt i=0; i<iElementArray.Count(); i++ )
        {
        if ( aUuid.FindC( iElementArray[i]->UUID() ) == 0 
             && aUuid.Length() == iElementArray[i]->UUID().Length() )
            {
            return i;
            }
        }
    return KErrNotFound;
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::RemoveL
// Remove device by given UID. Used for local devices only.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::RemoveL( const TDesC8& aUuid )
    {
    LOGSH( iHandle, "Devicelibrary RemoveL");

    TInt result = Find( aUuid );
    
    if ( result != KErrNotFound )
        {
        CUpnpDeviceLibraryElement* elem = iElementArray[result];

        iObserver.AdvertiseDeviceL( EFalse, *elem );
        iObserver.AdvertiseDeviceL( EFalse, *elem );        

        elem->AdvertisingFinished();
        
        elem->SetRenew( CUpnpTimeoutElement::ERemove );
        elem->SetTimeout( KLocalDeviceHysteresisWindow );
        elem->SetUpdateId( ++iUpdateId );
        elem->SetAlive( EFalse );            

        iObserver.DeviceListChangedL();
        }
    }
// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::RemoveSilentL
// Remove device by given UID. Used for local devices only.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::RemoveSilentL( const TDesC8& aUuid )
    {
    //LOGS( iHandle, "Devicelibrary RemoveSilentL");
    
   LOGS("CUpnpDeviceLibrary::RemoveSilentL( const TDesC8& aUuid )");
    TInt result = Find( aUuid );
    
    if ( result != KErrNotFound )
        {
        CUpnpDeviceLibraryElement* elem = iElementArray[result];

        
        elem->SetRenew( CUpnpTimeoutElement::ERemove );
        elem->SetTimeout( KLocalDeviceHysteresisWindow );
        elem->SetUpdateId( ++iUpdateId );
        elem->SetAlive( EFalse );            

        iObserver.DeviceListChangedL();
        }
         LOGS("CUpnpDeviceLibrary::RemoveSilentL( const TDesC8& aUuid ) passed");
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::TimeoutExpiredL
// Timeout expired for advertisement.
// -----------------------------------------------------------------------------
//
void CUpnpDeviceLibrary::TimeoutExpiredL( CUpnpTimeoutElement* anElement )
    {
    LOGSH( iHandle, "Devicelibrary TimeoutExpiredL");

    if ( !anElement )
        {
        return;
        }
    
    CUpnpDeviceLibraryElement* elem = 
        static_cast<CUpnpDeviceLibraryElement*>(anElement);
    
    switch ( elem->Renew() )
        {
        case CUpnpTimeoutElement::ERenew:
            {
            if ( !elem->Advertised() )
                {
                elem->Advertising();
                // whatever happends we don't want to have leave here
                TRAP_IGNORE( iObserver.AdvertiseDeviceL( EFalse, *elem ));
                TRAP_IGNORE( iObserver.AdvertiseDeviceL( EFalse, *elem ));
                elem->SetTimeout( KIntervalBetweenByeAndAlive );
                }
            else 
                {
                // whatever happends we don't want to have leave here
                TRAP_IGNORE( iObserver.AdvertiseDeviceL( ETrue, *elem ));
                elem->SetTimeout( KDeviceTimeout );
                }
            }
            break;
            
        case CUpnpTimeoutElement::EOnce: // after cache control expires
            {    
            // Set the timeout for the device to be removed
            iUpdateId++;
            elem->SetRenew( CUpnpTimeoutElement::ERemove );
            if( elem->Local() )
                {
                elem->SetTimeout( KLocalDeviceHysteresisWindow );    
                }
            else
                {
                elem->SetTimeout( KIpcCommunicationTimeout );
                }
            elem->SetUpdateId( iUpdateId );
            elem->SetAlive( EFalse );
            elem->SetExpired( ETrue );

            iObserver.DeviceListChangedL();
            }
            break;

        case CUpnpTimeoutElement::ERemove:
            {
            // Remove device from the element array
            for ( TInt i=0; i<iElementArray.Count(); i++ )
                {
                if ( elem == iElementArray[i] )
                    {
                    iElementArray.Remove( i );
                    iElementArray.Compress();
                    
                    delete elem;
                    break;
                    }
                }
            }
            break;

        default:
            break;
        }
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::DeviceList
// Return device list.
// -----------------------------------------------------------------------------
//
EXPORT_C RPointerArray<CUpnpDeviceLibraryElement>& CUpnpDeviceLibrary::DeviceList()
    {
    LOGSH( iHandle, "Devicelibrary DeviceList");

    return iElementArray;
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::GetUpdate
// Get update.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::GetUpdate( TInt& aUpdateId, 
                                         TUpnpDevice *&aDevices, 
                                         TUpnpService *&aServices,
                                         TInt& aDeviceCount,
                                         TInt& aServiceCount )
    {
    aDevices = NULL;
    aServices = NULL;
    
    TUpnpDevice* devices = NULL;
    TUpnpService* services = NULL;
    
    aDeviceCount = 0;
    aServiceCount = 0;
    
    for ( TInt i = 0; i < iElementArray.Count(); i++ )
        {
        CUpnpDeviceLibraryElement* elem = iElementArray[i];
        
        if (elem->UpdateId() > aUpdateId)
            {
            TUpnpDevice* temp = new TUpnpDevice[aDeviceCount+1];
            if ( !temp )
                {
                continue;
                }
            
            TPtr8 destPtr(reinterpret_cast<TUint8*>(temp), 
                0,
                sizeof(TUpnpDevice)*(aDeviceCount+1));
            TPtr8 srcPtr(reinterpret_cast<TUint8*>(devices),
                sizeof(TUpnpDevice)*aDeviceCount,
                sizeof(TUpnpDevice)*aDeviceCount);
            
            destPtr.Copy(srcPtr);
            
            if (devices)
                {
                delete[] devices;
                devices = NULL;
                }
            
            devices = temp;
            
            TUpnpDevice devtemp;
            destPtr.Set(reinterpret_cast<TUint8*>(&devices[aDeviceCount]),
                0,
                sizeof(TUpnpDevice));
            srcPtr.Set(reinterpret_cast<TUint8*>(&devtemp), 
                sizeof(TUpnpDevice),
                sizeof(TUpnpDevice));
            destPtr.Copy(srcPtr);
            
            devices[aDeviceCount].Set(elem);
            
            TInt servs = elem->ServiceList().Count();
            
            TUpnpService* tempServ = new TUpnpService[aServiceCount+servs];
            if ( !tempServ )
                {
                delete [] temp;
                temp = NULL;
                devices = NULL;
                continue;
                }
            
            destPtr.Set(reinterpret_cast<TUint8*>(tempServ), 
                0,
                sizeof(TUpnpService)*(aServiceCount+servs));
            srcPtr.Set(reinterpret_cast<TUint8*>(services),
                sizeof(TUpnpService)*aServiceCount,
                sizeof(TUpnpService)*aServiceCount);
            
            destPtr.Copy(srcPtr);
            
            if (services)
                {
                delete[] services;
                services = NULL;
                }
            
            services = tempServ;
            
            for (TInt j=0; j<servs; j++)
                {
                TUpnpService service;
                destPtr.Set(reinterpret_cast<TUint8*>(&services[aServiceCount]),
                    0,
                    sizeof(TUpnpService));
                srcPtr.Set(reinterpret_cast<TUint8*>(&service), 
                    sizeof(TUpnpService),
                    sizeof(TUpnpService));
                destPtr.Copy(srcPtr);
                
                services[aServiceCount].iServiceType.Zero();
                services[aServiceCount].iServiceType.Append(
                    elem->ServiceList()[j] );
                
                aServiceCount++;
                }
            
            aDeviceCount++;
            }
        }
    
    aUpdateId = iUpdateId;
    
    aDevices = devices;
    aServices = services;
    }
    
// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::RemoveAllDevicesL
// Removes all remote devices from device library.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::RemoveAllDevicesL()
    {
    for (TInt i = 0; i < iElementArray.Count(); i++)
        {
        CUpnpDeviceLibraryElement* elem = iElementArray[i];
        if ( elem->Alive() )
            {
            iUpdateId++;
            elem->SetAlive(EFalse);
            elem->SetUpdateId( iUpdateId );
            if( elem->Renew() == CUpnpTimeoutElement::EOnce )
                {
                elem->SetRenew( CUpnpTimeoutElement::ERemove );
                elem->SetTimeout( KIpcCommunicationTimeout );
                }
            }
        }
    iObserver.DeviceListChangedL();        
    }    

// -----------------------------------------------------------------------------
// TUpnpDevice::Set
// Set device.
// -----------------------------------------------------------------------------
//
EXPORT_C void TUpnpDevice::Set(CUpnpDeviceLibraryElement* aElem)
    {
    LOGS("Devicelibrary Set");

    if ( !aElem )
        {
        return;
        }

    iDescriptionURL = aElem->DescriptionUrl().Left( iDescriptionURL.MaxLength() );
    iUUID = aElem->UUID().Left( iUUID.MaxLength() );
    iDeviceType = aElem->DeviceType().Left( iDeviceType.MaxLength() );
    iDomain = aElem->Domain().Left( iDomain.MaxLength() );

    iServiceCount = aElem->ServiceList().Count();
    iLocal = aElem->Local();
    iRemote = EFalse;
    iAlive = aElem->Alive();
    iExpired = aElem->Expired();
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::AddDeviceL
// Add a device.
// -----------------------------------------------------------------------------
//
TBool CUpnpDeviceLibrary::AddDeviceL(CUpnpSsdpMessage *aMessage)
    {
    TBool update = EFalse;

    LOGSH( iHandle, "Devicelibrary AddDeviceL");

    if ( !aMessage )
        {
        return update;
        }
    
    TPtr8 uuidPtr = aMessage->Uuid();
    TInt index = Find( uuidPtr );
    
    if (index != KErrNotFound)
        {
        CUpnpDeviceLibraryElement* elem = iElementArray[index];
        if ( !elem->Local() )
        {
            if( elem->Renew() == CUpnpTimeoutElement::ERemove )
                {
                iElementArray.Remove( index );
                iElementArray.Compress();
                
                delete elem;
                
                CUpnpDeviceLibraryElement* element = CUpnpDeviceLibraryElement::NewL(*this);
                CleanupStack::PushL(element);
                
                update = element->AddInfoL(aMessage, iUpdateId);
                
                element->SetRenew(CUpnpTimeoutElement::EOnce);
                element->SetTimeout(aMessage->CacheControl());
                element->SetLocal(EFalse);
                iElementArray.Append(element);
                
                CleanupStack::Pop(); // element
                }
            
            else 
                {
                if(elem->Filter())
                    {
                    //Alive message from 
                    if(elem->AddInfoL(aMessage, iUpdateId))
                        {
                        update=ETrue;
                        }
                    elem->SetTimeout(aMessage->CacheControl());    
                    }
                else
                    {
                    iElementArray.Remove( index );
                    iElementArray.Compress();
                
                    delete elem;
                
                    CUpnpDeviceLibraryElement* element = CUpnpDeviceLibraryElement::NewL(*this);
                    CleanupStack::PushL(element);
                
                    update = element->AddInfoL(aMessage, iUpdateId);
                
                    element->SetRenew(CUpnpTimeoutElement::EOnce);
                    element->SetTimeout(aMessage->CacheControl());
                    element->SetLocal(EFalse);
                    iElementArray.Append(element);
                
                    CleanupStack::Pop(); // element
                    }
                }
            }
        }
    else
        {    
        CUpnpDeviceLibraryElement* element = CUpnpDeviceLibraryElement::NewL(*this);
        CleanupStack::PushL(element);
        
        update = element->AddInfoL(aMessage, iUpdateId);
        
        element->SetRenew(CUpnpTimeoutElement::EOnce);
        element->SetTimeout(aMessage->CacheControl());
        element->SetLocal(EFalse);
        iElementArray.Append(element);
        
        CleanupStack::Pop(); // element
        }
    return update;
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::RemoveDevice
// Remove a device.
// -----------------------------------------------------------------------------
//
void CUpnpDeviceLibrary::RemoveDeviceL( CUpnpSsdpMessage *aMessage )
    {
    LOGSH( iHandle, "Devicelibrary RemoveDevice" );

    if ( !aMessage )
        {
        return;
        }
    
    TPtr8 uuidPtr = aMessage->Uuid();
    TInt index = Find( uuidPtr );
    
    if ( index != KErrNotFound )
        {
        CUpnpDeviceLibraryElement* elem = iElementArray[index];
        if ( !elem->Local() )
            {
            if ( elem->Alive() )
                {
                iUpdateId++;
                elem->SetAlive(EFalse);
                elem->SetUpdateId( iUpdateId );
                if( elem->Renew() == CUpnpTimeoutElement::EOnce )
                    {
                    elem->SetRenew( CUpnpTimeoutElement::ERemove );
                    elem->SetTimeout( KIpcCommunicationTimeout );
                    }
                }
            iObserver.DeviceListChangedL();
            }
        }
    else
        {        
        CUpnpDeviceLibraryElement* element = CUpnpDeviceLibraryElement::NewL( *this );
        CleanupStack::PushL( element );

        element->AddInfoL( aMessage, iUpdateId );

        iUpdateId++;
        element->SetAlive( EFalse );
        element->SetUpdateId( iUpdateId );
        element->SetRenew( CUpnpTimeoutElement::ERemove );
        element->SetTimeout( KIpcCommunicationTimeout );
        element->SetLocal( EFalse );
        iElementArray.AppendL( element );
        
        CleanupStack::Pop( element );
        
        iObserver.DeviceListChangedL();
        }
    }

// -----------------------------------------------------------------------------
// CUpnpDeviceLibrary::StopFilteringDeviceL
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDeviceLibrary::StopFilteringDeviceL( const TDesC8& aUuid )
    {
    LOGSH( iHandle, "Devicelibrary RemoveDevice" );

    TInt index = Find( aUuid );
    
    if ( index != KErrNotFound )
        {
        CUpnpDeviceLibraryElement* elem = iElementArray[index];
        elem->SetFilter(EFalse);
        }
    }

//  End of File