mmappcomponents/mmmtpdataprovider/mmmtpdprequestprocessor/src/cgetobjectproplist.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:55:47 +0200
changeset 0 a2952bb97e68
child 9 bee149131e4b
child 25 d881023c13eb
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 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:  Implement the operation: GetObjectPropList
*
*/


#include <bautils.h>
#include <e32std.h>
#include <mtp/mmtpdataproviderframework.h>
#include <mtp/mmtpobjectmgr.h>
#include <mtp/cmtptypeobjectproplist.h>
#include <mtp/cmtptypestring.h>
#include <mtp/cmtptypearray.h>
#include <mtp/cmtpobjectmetadata.h>

#include "tobjectdescription.h"
#include "cgetobjectproplist.h"
#include "tmmmtpdppanic.h"
#include "mmmtpdplogger.h"
#include "mmmtpdpconfig.h"
#include "mmmtpdputility.h"
#include "cmmmtpdpmetadataaccesswrapper.h"

static const TUint32 KMTPAll = 0xffffffff;
_LIT( KMtpDateTimeFormat, "%F%Y%M%DT%H%T%S" );
const TInt KMtpMaxDateTimeStringLength = 15;

#if defined(_DEBUG) || defined(MMMTPDP_PERFLOG)
_LIT( KSubFormatParse, "SubFormatParse" );
#endif

// Verification data for the getObjectPropList request
const TMTPRequestElementInfo KMTPGetObjectPropListPolicy[] =
    {
        {
        TMTPTypeRequest::ERequestParameter1,
        EMTPElementTypeObjectHandle,
        EMTPElementAttrNone,
        2,
        KMTPHandleAll,
        KMTPHandleNone
        }
    };

// -----------------------------------------------------------------------------
// CGetObjectPropList::~CGetObjectPropList
// Destructor
// -----------------------------------------------------------------------------
//
EXPORT_C CGetObjectPropList::~CGetObjectPropList()
    {
    Cancel();

    delete iObject;
    delete iPropertyList;
    delete iPropertyElement;
    iHandles.Close();
    iPropertyArray.Close();

#if defined(_DEBUG) || defined(MMMTPDP_PERFLOG)
    delete iPerfLog;
#endif
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::CGetObjectPropList
// Standard C++ Constructor
// -----------------------------------------------------------------------------
//
EXPORT_C CGetObjectPropList::CGetObjectPropList( MMTPDataProviderFramework& aFramework,
    MMTPConnection& aConnection,
    MMmMtpDpConfig& aDpConfig ) :
    CRequestProcessor( aFramework,
        aConnection,
        sizeof ( KMTPGetObjectPropListPolicy ) / sizeof( TMTPRequestElementInfo ),
        KMTPGetObjectPropListPolicy ),
    iHandles ( KMmMtpRArrayGranularity ),
    iDpConfig( aDpConfig ),
    iPropertyArray( KMmMtpRArrayGranularity )
    {
    PRINT( _L( "Operation: GetObjectPropList(0x9805)" ) );
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::ConstructL
// 2nd Phase Constructor
// -----------------------------------------------------------------------------
//
EXPORT_C void CGetObjectPropList::ConstructL()
    {
    CActiveScheduler::Add( this );

    iPropertyList = CMTPTypeObjectPropList::NewL();

    SetPSStatus();

#if defined(_DEBUG) || defined(MMMTPDP_PERFLOG)
    iPerfLog = CMmMtpDpPerfLog::NewL( _L( "MediaMtpDataProviderEnumerator" ) );
#endif
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::CheckRequestL
// Verify the reqeust and returns it
// -----------------------------------------------------------------------------
//
EXPORT_C TMTPResponseCode CGetObjectPropList::CheckRequestL()
    {
    PRINT( _L( "MM MTP => CGetObjectPropList::CheckRequestL" ) );

    TMTPResponseCode result = CRequestProcessor::CheckRequestL();

    if ( result == EMTPRespCodeOK )
        {
        result = CheckFormatL();
        }

    if ( result == EMTPRespCodeOK )
        {
        result = CheckDepth();
        }

    if ( result == EMTPRespCodeOK )
        {
        result = CheckPropCodeL();
        }

    PRINT1( _L("MM MTP <= CGetObjectPropList::CheckRequestL, response 0x%x"), result );

    return result;
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::ServiceL
// service a request at request phase
// -----------------------------------------------------------------------------
//
EXPORT_C void CGetObjectPropList::ServiceL()
    {
    GetObjectHandlesL();
    TInt numOfObjects = iHandles.Count();
    PRINT2( _L( "MM MTP <> CGetObjectPropList::ServiceL, numOfObjects = %d, iPropCode = 0x%x" ),
        numOfObjects,
        iPropCode );
    if ( numOfObjects > 0
        && ( iPropCode == KMTPAll || iPropCode == EMTPObjectPropCodeUndefined ) )
        {
        // iFirstUnprocessed = 0;
        TRequestStatus* status = &iStatus;
        User::RequestComplete( status, iStatus.Int() );
        SetActive();
        }
    else
        {
        TInt err = KErrNone;
        for ( TInt i = 0; i < numOfObjects; i++ )
            {
            TUint32 handle = iHandles[i ];

            if ( iFramework.ObjectMgr().ObjectOwnerId( handle )
                == iFramework.DataProviderId() )
                {
                delete iObject;
                iObject = NULL;
                iObject = CMTPObjectMetaData::NewL();

                if ( iFramework.ObjectMgr().ObjectL( handle, *iObject ) ) // Populate the object meta data
                    {
                    SetSubFormatCodeL();

                    err = ServiceOneObjectPropertyL( handle, iPropCode );
                    if ( err != KErrNone && err != KErrNotSupported )
                        break;
                    }
                }
            }
        PRINT1( _L( "MM MTP <> CGetObjectPropList::ServiceL, one property was queried, Send data to PC! err = %d" ), err );
        if ( err == KErrNone  || err == KErrNotSupported )
            SendDataL( *iPropertyList );
        else if ( err == KErrNotFound )
            SendResponseL( EMTPRespCodeAccessDenied );
        else
            SendResponseL( EMTPRespCodeGeneralError );
        }

    PRINT( _L( "MM MTP <= CGetObjectPropList::ServiceL" ) );
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::CheckFormatL
// Ensures the object format operation parameter is valid
// -----------------------------------------------------------------------------
//
TMTPResponseCode CGetObjectPropList::CheckFormatL() const
    {
    TMTPResponseCode response = EMTPRespCodeOK;

    PRINT( _L( "MM MTP => CGetObjectPropList::CheckFormatL" ) );

    TUint32 formatCode = Request().Uint32( TMTPTypeRequest::ERequestParameter2 );
    iFormatCode = formatCode;

    PRINT1( _L( "MM MTP <> CGetObjectPropList::CheckFormatL formatCode = 0x%x" ), formatCode );

    if ( formatCode != 0 )
        {
        response = EMTPRespCodeInvalidObjectFormatCode;

        const RArray<TUint>* format = iDpConfig.GetSupportedFormat();
        TInt count = format->Count();

        for ( TInt i = 0; i < count; i++ )
            {
            if ( formatCode == (*format)[i ] )
                {
                response = EMTPRespCodeOK;
                break;
                }
            }
        }

    PRINT1( _L( "MM MTP <= CGetObjectPropList::CheckFormatL, response 0x%x" ), response );

    return response;
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::CheckPropCodeL
// Ensures the object prop code operation parameter is valid
// -----------------------------------------------------------------------------
//
TMTPResponseCode CGetObjectPropList::CheckPropCodeL() const
    {
    PRINT( _L( "MM MTP => CGetObjectPropList::CheckPropCode" ) );

    TMTPResponseCode response = EMTPRespCodeOK;

    TUint32 formatCode = Request().Uint32( TMTPTypeRequest::ERequestParameter2 );
    iPropCode = Request().Uint32( TMTPTypeRequest::ERequestParameter3 );
    PRINT1( _L( "MM MTP <> CGetObjectPropList::CheckPropCode, iPropCode = 0x%x" ), iPropCode );

    iPropertyArray.Reset();
    if ( iPropCode == EMTPObjectPropCodeUndefined )
        {
        // Get group code
        TUint32 groupCode = Request().Uint32( TMTPTypeRequest::ERequestParameter4 );
        PRINT1( _L( "MM MTP <> CGetObjectPropList::CheckPropCode, Group Code = 0x%x" ), groupCode );

        // check if groupCode is supported
        TInt count = sizeof ( KSupportedGroupCode )
            / sizeof ( KSupportedGroupCode[0] );
        TInt i = 0;
        for ( ; i < count; i++ )
            {
            if ( groupCode == KSupportedGroupCode[i ] )
                break;
            }
        if ( i == count )
            response = EMTPRespCodeSpecificationByGroupUnsupported;

        }
    else if ( iPropCode != KMTPAll )
        {
        PRINT1( _L( "MM MTP <> CGetObjectPropList::CheckPropCode, Property(0x%x) was queried." ), iPropCode );

        TInt err = KErrNone;
        const RArray<TUint>* properties = NULL;
        if ( formatCode == KMTPFormatsAll )
            properties = iDpConfig.GetAllSupportedProperties();
        else
            properties = iDpConfig.GetSupportedPropertiesL( formatCode );

        User::LeaveIfError( err );

        const TInt count = properties->Count();
        TInt i = 0;
        for ( i = 0; i < count; i++ )
            {
            if ( (*properties)[i] == iPropCode )
                {
                iPropertyArray.Append( iPropCode );
                break;
                }
            }

        if ( iPropertyArray.Count() == 0 )
            {
            response = EMTPRespCodeInvalidObjectPropCode;
            }
        }
    else
        {
        PRINT( _L( "MM MTP <> CGetObjectPropList::CheckPropCode, All properties were queried." ) );
        }

    PRINT1( _L( "MM MTP <= CGetObjectPropList::CheckPropCode, response 0x%x" ), response );

    return response;
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::CheckDepth
// Ensures the depth operation parameter is valid
// -----------------------------------------------------------------------------
//
TMTPResponseCode CGetObjectPropList::CheckDepth() const
    {
    TMTPResponseCode response = EMTPRespCodeSpecificationByDepthUnsupported;

    PRINT( _L( "MM MTP => CGetObjectPropList::CheckDepth" ) );

    // We either support no depth at all, or 1 depth or (0xFFFFFFFF) with objecthandle as 0x00000000
    iHandle = Request().Uint32( TMTPTypeRequest::ERequestParameter1 );
    PRINT1( _L( "MM MTP <> CGetObjectPropList::CheckDepth iHandle = 0x%x" ), iHandle );
    iDepth = Request().Uint32( TMTPTypeRequest::ERequestParameter5 );
    PRINT1( _L( "MM MTP <> CGetObjectPropList::CheckDepth iDepth = 0x%x" ), iDepth );

    if ( ( iDepth == 0 )
        || ( iDepth == 1 )
        || ( iDepth == KMTPAll && iHandle == KMTPHandleNone ) )
        {
        response = EMTPRespCodeOK;
        }

    PRINT1( _L( "MM MTP <= CGetObjectPropList::CheckDepth, response 0x%x" ), response );

    return response;
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::GetObjectHandlesL
// Gets the handles for the objects that we want the properties for
// -----------------------------------------------------------------------------
//
void CGetObjectPropList::GetObjectHandlesL()
    {
    PRINT( _L( "MM MTP => CGetObjectPropList::GetObjectHandlesL" ) );

    iHandles.Reset();

    if ( iHandle == KMTPHandleAll )
        {
        GetObjectHandlesL( KMTPStorageAll, KMTPHandleNone );
        }
    else if ( iHandle == KMTPHandleNone )
        {
        GetRootObjectHandlesL( iDepth, iFormatCode );
        }
    else
        {
        CMTPObjectMetaData* meta( iRequestChecker->GetObjectInfo( iHandle ) );
        TPtrC suid( meta->DesC( CMTPObjectMetaData::ESuid ) );
        TParsePtrC parse( suid );
        iDpConfig.GetWrapperL().SetStorageRootL( parse.Drive() );

        __ASSERT_DEBUG( meta, Panic( EMmMTPDpObjectNull ) );

        if ( ( meta->Uint( CMTPObjectMetaData::EFormatCode ) == EMTPFormatCodeAssociation )
            && ( meta->Uint( CMTPObjectMetaData::EFormatSubCode ) == EMTPAssociationTypeGenericFolder ) )
            {
            GetFolderObjectHandlesL( iDepth, iHandle );
            }
        else
            {
            iHandles.Append( iHandle );
            }
        }

    PRINT( _L( "MM MTP <= CGetObjectPropList::GetObjectHandlesL" ) );
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::GetObjectHandlesL
// Gets all object handles (for GetObjectHandlesL)
// -----------------------------------------------------------------------------
//
void CGetObjectPropList::GetObjectHandlesL( TUint32 aStorageId,
    TUint32 aParentHandle,
    TUint16 aFormat /*= 0x0000*/)
    {
    PRINT2( _L( "MM MTP => CGetObjectPropList::GetObjectHandlesL, aStorageId = 0x%x, aParentHandle = 0x%x" ),
        aStorageId,
        aParentHandle );

    RMTPObjectMgrQueryContext context;
    CleanupClosePushL( context ); // + context

    iHandles.Reset();

    if ( aFormat )
        {
        TMTPObjectMgrQueryParams params( aStorageId, aFormat, aParentHandle );

        do
            {
            iFramework.ObjectMgr().GetObjectHandlesL( params, context, iHandles );
            }
        while ( !context.QueryComplete() );
        }
    else
        {
        TMTPObjectMgrQueryParams params( aStorageId,
            KMTPFormatsAll,
            aParentHandle );

        do
            {
            RArray<TUint> handles;
            CleanupClosePushL( handles ); // + handles
            iFramework.ObjectMgr().GetObjectHandlesL( params, context, handles );

            for ( TInt i = 0; i < handles.Count(); i++ )
                {
                if ( iFramework.ObjectMgr().ObjectOwnerId( handles[i ] )
                    == iFramework.DataProviderId() )
                    iHandles.Append( handles[i ] );
                }
            CleanupStack::PopAndDestroy( &handles ); // - handles
            }
        while ( !context.QueryComplete() );
        }

    CleanupStack::PopAndDestroy( &context ); // - context

    PRINT( _L( "MM MTP <= CGetObjectPropList::GetObjectHandlesL" ) );
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::GetFolderObjectHandlesL
// Gets a folder object handle
// -----------------------------------------------------------------------------
//
void CGetObjectPropList::GetFolderObjectHandlesL( TUint32 aDepth,
    TUint32 aParentHandle )
    {
    PRINT2( _L( "MM MTP => CGetObjectPropList::GetFolderObjectHandlesL aDepth = 0x%x, aParentHandle = 0x%x" ),
        aDepth,
        aParentHandle );

    if ( aDepth == 0 )
        {
        // Do Nothing!!!
        }
    else //if(aDepth ==1)
        {
        GetObjectHandlesL( KMTPStorageAll, aParentHandle, iFormatCode );
        }

    PRINT( _L( "MM MTP <= CGetObjectPropList::GetFolderObjectHandlesL" ) );
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::GetRootObjectHandlesL
// Gets the handles of all objects on the root
// -----------------------------------------------------------------------------
//
void CGetObjectPropList::GetRootObjectHandlesL( TUint32 aDepth, TUint16 aFormat )
    {
    PRINT2( _L( "MM MTP => CGetObjectPropList::GetRootObjectHandlesL aDepth = 0x%x, aFormat = 0x%x" ), aDepth , aFormat);

    if ( aDepth == KMaxTUint )
        {
        GetObjectHandlesL( KMTPStorageAll, KMTPHandleNone, aFormat );
        }
    else if ( aDepth == 0 )
        {
        // Return empty set to PC according to spec 0.96, folder is handled by device dp
        iHandles.Reset();
        }
    else
        {
        GetObjectHandlesL( KMTPStorageAll, KMTPHandleNoParent, aFormat );
        }

    PRINT( _L( "MM MTP <= CGetObjectPropList::GetRootObjectHandlesL" ) );
    }
// -----------------------------------------------------------------------------
// CGetObjectPropList::ServiceAllPropertiesL
//  Gets a list of all the properties supported
// -----------------------------------------------------------------------------
//
TInt CGetObjectPropList::ServiceAllPropertiesL( TUint32 aHandle )
    {
    PRINT1( _L( "MM MTP => CGetObjectPropList::ServiceAllPropertiesL aHandle = 0x%x" ), aHandle );

    // Append the current object info onto our list
    TFileName fileName( iObject->DesC( CMTPObjectMetaData::ESuid ) );
    TUint32 formatCode = iObject->Uint( CMTPObjectMetaData::EFormatCode );

    PRINT2( _L( "MM MTP <> CGetObjectPropList::ServiceAllPropertiesL, fileName = %S, formatCode = 0x%x" ),
        &fileName,
        formatCode );

    const RArray<TUint>* properties = NULL;
    if ( formatCode == KMTPFormatsAll )
        properties = iDpConfig.GetAllSupportedProperties();
    else
        properties = iDpConfig.GetSupportedPropertiesL( formatCode );

    const TInt count = properties->Count();

    TInt err = KErrNone;
    for ( TInt i = 0; i < count; i++ )
        {
        // no need to do the trap anymore, this is being handle internally in Media DP's ServiceSpecificObjectPropertyL, also, this base class should not know too much of different handling between different formats
        err = ServiceOneObjectPropertyL( aHandle, (*properties)[i] );
        if ( err == KErrNotSupported )  // Skip
            err = KErrNone;
        if ( err != KErrNone )
            break;
        }

    PRINT1( _L( "MM MTP <= CGetObjectPropList::ServiceAllPropertiesL err = %d" ), err );
    return err;
    }

TInt CGetObjectPropList::ServiceGroupPropertiesL( TUint32 aHandle )
    {
    PRINT1( _L( "MM MTP => CGetObjectPropList::ServiceGroupPropertiesL aHandle = 0x%x" ), aHandle );

    TFileName fileName( iObject->DesC( CMTPObjectMetaData::ESuid ) );
    TUint32 formatCode = iObject->Uint( CMTPObjectMetaData::EFormatCode );

    PRINT2( _L( "MM MTP <> CGetObjectPropList::ServiceGroupPropertiesL, fileName = %S, formatCode = 0x%x" ),
        &fileName,
        formatCode );

    // Get property codes according to group code, put those into array
    TUint32 groupCode = Request().Uint32( TMTPTypeRequest::ERequestParameter4 );
    iPropertyArray.Reset();
    GetPropertiesL( iPropertyArray, groupCode, formatCode );

    const TInt count = iPropertyArray.Count();

    TInt err = KErrNone;
    for ( TInt i = 0; i < count; i++ )
        {
        err = ServiceOneObjectPropertyL( aHandle, iPropertyArray[i] );
        if ( err == KErrNotSupported )  // Skip
            err = KErrNone;
        if ( err != KErrNone )
            break;
        }

    PRINT1( _L( "MM MTP <= CGetObjectPropList::ServiceGroupPropertiesL err = %d" ), err );

    return err;
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::ServiceOneObjectPropertyL
//  Gets the object property information for the required object
// -----------------------------------------------------------------------------
//
TInt CGetObjectPropList::ServiceOneObjectPropertyL( TUint32 aHandle,
    TUint16 aPropCode )
    {
    PRINT2( _L( "MM MTP => CGetObjectPropList::ServiceOneObjectPropertyL aHandle = 0x%x, aPropCode = 0x%x" ),
        aHandle,
        aPropCode );
    CMTPTypeString* textData = NULL;
    TInt err = KErrNone;

    if ( iPropertyElement != NULL )
        {
        delete iPropertyElement;
        iPropertyElement = NULL;
        }

    switch ( aPropCode )
        {
        // Storage ID
        case EMTPObjectPropCodeStorageID:
            {
            TMTPTypeUint32 storageId( iObject->Uint( CMTPObjectMetaData::EStorageId ) );
            iPropertyElement = &(iPropertyList->ReservePropElemL(aHandle, aPropCode));
            iPropertyElement->SetUint32L(CMTPTypeObjectPropListElement::EValue, storageId.Value());
            }
            break;

        // Format Code
        case EMTPObjectPropCodeObjectFormat:
            {
            TMTPTypeUint16 objectFormat( iObject->Uint( CMTPObjectMetaData::EFormatCode ) );
            iPropertyElement = &(iPropertyList->ReservePropElemL(aHandle, aPropCode));
            iPropertyElement->SetUint16L(CMTPTypeObjectPropListElement::EValue, objectFormat.Value());
            }
            break;

        // Protection Status
        case EMTPObjectPropCodeProtectionStatus:
            {
            TMTPTypeUint16 protectionStatus( MmMtpDpUtility::GetProtectionStatusL( iFramework.Fs(),
                iObject->DesC( CMTPObjectMetaData::ESuid ) ) );
            iPropertyElement = &(iPropertyList->ReservePropElemL(aHandle, aPropCode));
            iPropertyElement->SetUint16L(CMTPTypeObjectPropListElement::EValue,  protectionStatus.Value());
            }
            break;

        // Object Size
        case EMTPObjectPropCodeObjectSize:
            {
            TMTPTypeUint64 objectSize( MmMtpDpUtility::GetObjectSizeL( iFramework.Fs(),
                iObject->DesC( CMTPObjectMetaData::ESuid ) ) );
            iPropertyElement = &( iPropertyList->ReservePropElemL( aHandle, aPropCode ) );
            iPropertyElement->SetUint64L( CMTPTypeObjectPropListElement::EValue, objectSize.Value() );
            }
            break;

        // Filename
        case EMTPObjectPropCodeObjectFileName:
            {
#ifdef  _DEBUG
            HBufC* log = iObject->DesC( CMTPObjectMetaData::ESuid ).Alloc();
            PRINT1( _L( "MM MTP <> CGetObjectPropList::ServiceOneObjectPropertyL FileName = %S" ), log );
            delete log;
            log = NULL;
#endif // _DEBUG
            TParsePtrC parse( iObject->DesC( CMTPObjectMetaData::ESuid ) );
            textData = CMTPTypeString::NewLC( parse.NameAndExt() );    // + textData
            iPropertyElement = &(iPropertyList->ReservePropElemL(aHandle, aPropCode));
            iPropertyElement->SetStringL(CMTPTypeObjectPropListElement::EValue, textData->StringChars());
            CleanupStack::PopAndDestroy( textData );    // - textData
            }
            break;

        // Parent Object
        case EMTPObjectPropCodeParentObject:
            {
            iPropertyElement = &(iPropertyList->ReservePropElemL(aHandle, aPropCode));
            iPropertyElement->SetUint32L(CMTPTypeObjectPropListElement::EValue, iObject->Uint( CMTPObjectMetaData::EParentHandle ));
            }
            break;

        // PUID
        case EMTPObjectPropCodePersistentUniqueObjectIdentifier:
            {
            TMTPTypeUint128 puid = iFramework.ObjectMgr().PuidL( aHandle );
            iPropertyElement = &(iPropertyList->ReservePropElemL(aHandle, aPropCode));
            iPropertyElement->SetUint128L(CMTPTypeObjectPropListElement::EValue,puid.UpperValue(), puid.LowerValue() );
            }
            break;

        case EMTPObjectPropCodeName:
        case EMTPObjectPropCodeDateAdded:
            {
            if ( ( aPropCode == EMTPObjectPropCodeName )
                || ( ( !MmMtpDpUtility::IsVideoL( iObject->DesC( CMTPObjectMetaData::ESuid ), iFramework ) )
                && ( aPropCode == EMTPObjectPropCodeDateAdded ) ) )
                {
                textData = CMTPTypeString::NewLC();   // + textData

                TRAP( err, iDpConfig.GetWrapperL().GetObjectMetadataValueL( aPropCode,
                    *textData,
                    *iObject ) );

                PRINT1( _L( "MM MTP <> CGetObjectPropList::ServiceOneObjectPropertyL err = %d" ), err );

                if ( err == KErrNone )
                    {
                    iPropertyElement = &(iPropertyList->ReservePropElemL( aHandle, aPropCode ) );
                    iPropertyElement->SetStringL(CMTPTypeObjectPropListElement::EValue, textData->StringChars());
                    }

                CleanupStack::PopAndDestroy( textData );  // - textData
                }
            }
            break;

        case EMTPObjectPropCodeDateCreated:
        case EMTPObjectPropCodeDateModified:
            {
            TTime dataModified;
            dataModified = MmMtpDpUtility::GetObjectDateModifiedL( iFramework.Fs(),
                iObject->DesC( CMTPObjectMetaData::ESuid ) );

            TBuf<KMtpMaxDateTimeStringLength> timeStr;
            dataModified.FormatL( timeStr, KMtpDateTimeFormat );
            PRINT1( _L( "MM MTP <> CGetObjectPropList::ServiceOneObjectPropertyL Date time %S" ), &timeStr );
            CMTPTypeString* datastring = CMTPTypeString::NewLC( timeStr );  // + datastring

            iPropertyElement = &(iPropertyList->ReservePropElemL(aHandle, aPropCode));
            iPropertyElement->SetStringL(CMTPTypeObjectPropListElement::EValue, datastring->StringChars());
            CleanupStack::PopAndDestroy( datastring );  // - datastring
            }
            break;

        // Consumable Value
        case EMTPObjectPropCodeNonConsumable:
            {
            iPropertyElement = &(iPropertyList->ReservePropElemL(aHandle, aPropCode));
            iPropertyElement->SetUint8L(CMTPTypeObjectPropListElement::EValue,0);
            }
            break;

        default:
            {
            err = ServiceSpecificObjectPropertyL( aPropCode, aHandle );
            }
            break;
        }

    if ( iPropertyElement != NULL )
        {
        iPropertyList->CommitPropElemL( *iPropertyElement );
        iPropertyElement = NULL;
        }

    PRINT( _L( "MM MTP <= CGetObjectPropList::ServiceOneObjectPropertyL" ) );

    return err;
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::RunL
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CGetObjectPropList::RunL()
    {
    if ( iHandles.Count() > 0 )
        {
        TInt err = KErrNone;
        TUint32 handle = iHandles[0];
        PRINT1( _L( "MM MTP <> CGetObjectPropList::RunL, handle = 0x%x" ), handle );
        if ( iFramework.ObjectMgr().ObjectOwnerId( handle )
            == iFramework.DataProviderId() )
            {
            delete iObject;
            iObject = NULL;
            iObject = CMTPObjectMetaData::NewL();

            if ( iFramework.ObjectMgr().ObjectL( handle, *iObject ) ) // Populate the object meta data
                {
                SetSubFormatCodeL();

                if ( iPropCode == KMTPAll )
                    err = ServiceAllPropertiesL( handle );
                else if ( iPropCode == EMTPObjectPropCodeUndefined )
                    {
                    err = ServiceGroupPropertiesL( handle );
                    }
                if ( err == KErrNotFound )
                    SendResponseL( EMTPRespCodeAccessDenied );
                else if ( err != KErrNone )
                    SendResponseL( EMTPRespCodeGeneralError );
                }
            }

        // Complete ourselves with current TRequestStatus
        // Increase index to process next handle on next round
        // iFirstUnprocessed++;
        iHandles.Remove( 0 );

        if ( err == KErrNone )
            {
            TRequestStatus* status = &iStatus;
            User::RequestComplete( status, iStatus.Int() );
            SetActive();
            }
        }
    else // all handles processed, can send data
        {
        PRINT( _L( "MM MTP <> CGetObjectPropList::RunL, Finished, Send data to PC!" ) );
        SendDataL( *iPropertyList );
        }
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::RunError
//
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CGetObjectPropList::RunError( TInt aError )
    {
    PRINT1( _L( "MM MTP <> CGetObjectPropList::RunError aError = %d" ), aError );
        TRAP_IGNORE( SendResponseL( EMTPRespCodeGeneralError ) );

    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CGetObjectPropList::DoCancel()
// Cancel the process
// -----------------------------------------------------------------------------
//
EXPORT_C void CGetObjectPropList::DoCancel()
    {
    // TODO: need to send the data here?
    // SendDataL( *iPropertyList );
    }

void CGetObjectPropList::GetPropertiesL( RArray<TUint>& aPropArray,
    TUint32 aGroupCode,
    TUint16 aFormatCode ) const
    {
    aPropArray.Reset();

    const RArray<TUint>* properties = NULL;
    if ( aFormatCode == KMTPFormatsAll )
        properties = iDpConfig.GetAllSupportedProperties();
    else
        properties = iDpConfig.GetSupportedPropertiesL( aFormatCode );

    TInt count = sizeof ( KPropGroupMapTable ) / sizeof ( KPropGroupMapTable[0] );
    for ( TInt i = 0; i < properties->Count(); i++ )
        {
        for ( TInt j = 0; j < count; j++ )
            {
            if( KPropGroupMapTable[j].iPropCode == (*properties)[i]
                && KPropGroupMapTable[j].iGroupCode == aGroupCode )
                {
                aPropArray.Append( (*properties)[i] );
                break;
                }
            }
        }
    }

void CGetObjectPropList::SetSubFormatCodeL()
    {
    __ASSERT_DEBUG( iObject, Panic( EMmMTPDpObjectNull ) );

    // Note: Parsing out subformat code in external enumeration phase.
    //       This process was delayed in internal phase to avoid time-out.
    TUint16 formatCode = iObject->Uint( CMTPObjectMetaData::EFormatCode );
    TUint subFormatCode = iObject->Uint( CMTPObjectMetaData::EFormatSubCode );
    PRINT2( _L( "MM MTP <> CGetObjectPropList::SetSubFormatCode, formatCode = 0x%x, subFormatCode = 0x%x" ),
        formatCode,
        subFormatCode );

    TBool ifNeedParse = ( ( formatCode == EMTPFormatCodeMP4Container )
        || ( formatCode == EMTPFormatCode3GPContainer )
        || ( formatCode== EMTPFormatCodeASF ) )
        && ( subFormatCode == EMTPSubFormatCodeUnknown );
    if ( ifNeedParse )
        {
        PERFLOGSTART( KSubFormatParse );

        if ( MmMtpDpUtility::IsVideoL( iObject->DesC( CMTPObjectMetaData::ESuid ) ) )
            subFormatCode = EMTPSubFormatCodeVideo;
        else
            subFormatCode = EMTPSubFormatCodeAudio;

        PERFLOGSTOP( KSubFormatParse );

        iObject->SetUint( CMTPObjectMetaData::EFormatSubCode, subFormatCode );
        }
    }

// end of file