/*
* Copyright (c) 2007 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:  This class is used by db plugin for all database related
*                functionality. The main responsibilities are:
*
*/


// INCLUDE FILES
#include <bautils.h>
#ifdef RD_MULTIPLE_DRIVE
#include <driveinfo.h>
#include <pathinfo.h>
#endif //RD_MULTIPLE_DRIVE

#include <mpxcollectiondbhgres.rsg>
#include <mpxmediageneraldefs.h>
#include <mpxmediacontainerdefs.h>
#include <mpxmediamusicdefs.h>
#include <mpxmediaaudiodefs.h>
#include <mpxmedia.h>
#include <mpxmediaarray.h>
#include <mpxcollectionpath.h>
#include <mpxlog.h>

#include "mpxresource.h"
#include "mpxdbcommonutil.h"

#include "mpxdbutil.h"
#include "mpxcollectiondbdef.h"
#include "mpxdbpluginqueries.h"
#include "mpxcollectiondbmanager.h"
#include "mpxdbplaylist.h"
#include "mpxdbplaylistsongs.h"
#include "mpxdbcategory.h"
#include "mpxdbauxiliary.h"
#include "mpxdbautoplaylist.h"
#include "mpxdbhandler.h"
#include "mpxdbartist.h"
#include "mpxdbalbum.h"
#include "mpxdbgenre.h"
#include "mpxdbcomposer.h"

// CONSTANTS
_LIT(KMPXVirtualPlaylistExt, ".vir");
static const TInt KMaxOpInTransaction = 100;

const TInt KSqlDbCorrupted = -321;

#if defined (__MTP_PROTOCOL_SUPPORT)

#include <centralrepository.h>

// MTP CenRep Key UID
const TUid KMPXMtpSettings = {0x101FFC53};
// MTP CenRep Key for Delete contents
const TUint32 KMPXMtpSaveDeletedRecordFlag = 0x00000001;

#endif

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

// ----------------------------------------------------------------------------
// Two-phased constructor.
// ----------------------------------------------------------------------------
//
CMPXDbHandler* CMPXDbHandler::NewL(
    RFs& aFs,
    CMPXResource& aResource)
    {
    MPX_FUNC("CMPXDbHandler::NewL");
    CMPXDbHandler* self = CMPXDbHandler::NewLC(aFs, aResource);
    CleanupStack::Pop(self);
    return self;
    }

// ----------------------------------------------------------------------------
// Two-phased constructor.
// ----------------------------------------------------------------------------
//
CMPXDbHandler* CMPXDbHandler::NewLC(
    RFs& aFs,
    CMPXResource& aResource)
    {
    MPX_FUNC("CMPXDbHandler::NewLC");
    CMPXDbHandler* self = new (ELeave) CMPXDbHandler(aFs, aResource);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

// ----------------------------------------------------------------------------
// Destructor
// ----------------------------------------------------------------------------
//
CMPXDbHandler::~CMPXDbHandler()
    {
    MPX_FUNC("CMPXDbHandler::~CMPXDbHandler");

    delete iAutoPlaylist;

    delete iDbMusic;
    delete iDbPlaylist;
    delete iDbArtist;
    delete iDbAlbum;
    delete iDbGenre;
    delete iDbComposer;
    delete iDbAuxiliary;
    delete iDbManager;
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    delete iDbAbstractAlbum;
#endif // ABSTRACTAUDIOALBUM_INCLUDED
    delete iMimeTypes;
    delete iExtensions;
    delete iExtensionsMime;
    delete iExtensionsDrm;

    iDbDrives.Close();
    }

// ----------------------------------------------------------------------------
// C++ default constructor can NOT contain any code, that might leave
// ----------------------------------------------------------------------------
//
CMPXDbHandler::CMPXDbHandler(
    RFs& aFs,
    CMPXResource& aResource) :
    iFs(aFs),
    iResource(aResource)
    {
    MPX_FUNC("CMPXDbHandler::CMPXDbHandler");
    }

// ----------------------------------------------------------------------------
// Symbian 2nd phase constructor can leave.
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::ConstructL()
    {
    MPX_FUNC("CMPXDbHandler::ConstructL");

    iMimeTypes = iResource.ReadDesCArrayL(R_MC_MIME_TYPES);
    iExtensions = iResource.ReadDesCArrayL(R_MC_MUSIC_FILE_EXTENSIONS);
    iExtensionsMime = iResource.ReadDesCArrayL(R_MC_FILE_EXTENSIONS_MIME);
    iExtensionsDrm = iResource.ReadDesCArrayL(R_MC_FILE_EXTENSIONS_DRM);

    // make sure all databases are created and valid
    iDbManager = CMPXCollectionDbManager::NewL(iFs);

    CDesCArrayFlat* musicFolders =
#ifdef RD_MULTIPLE_DRIVE
        GetMusicFoldersL();
#else
        iResource.ReadDesCArrayL(R_MC_DEFAULT_MUSIC_FOLDERS);
#endif

    // create the music folders and initialize iDbDrives
    CleanupStack::PushL(musicFolders);
    ProcessMusicFoldersL(*musicFolders);
    CleanupStack::PopAndDestroy(musicFolders);

    // Create the db infrastructure,
    //
    iDbMusic = CMPXDbMusic::NewL(*iDbManager, iResource, *this);
    iDbPlaylist = CMPXDbPlaylist::NewL(*iDbManager, *this);
    iDbArtist = CMPXDbArtist::NewL(*iDbManager, EMPXArtist, *this);
    iDbAlbum = CMPXDbAlbum::NewL(*iDbManager, EMPXAlbum, *this);
    iDbGenre = CMPXDbGenre::NewL(*iDbManager, EMPXGenre);
    iDbComposer = CMPXDbComposer::NewL(*iDbManager, EMPXComposer);
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    iDbAbstractAlbum = CMPXDbAbstractAlbum::NewL(*iDbManager, EMPXAbstractAlbum, iFs);
#endif // ABSTRACTAUDIOALBUM_INCLUDED
    iAutoPlaylist = CMPXDbAutoPlaylist::NewL(*iDbManager, iFs, iResource);
    iDbAuxiliary = CMPXDbAuxiliary::NewL(*iDbManager);

    MPX_TRAPD(err, iDbManager->InitDatabasesL(iDbDrives));

    // If KErrCorrupt is returned, a database file was found to be corrupted
    // and was replaced with a new one.  The db plugin can ignore this error and continue
    // because a new db file was successfully created in a subsequent retry.
    if ((err != KErrNone) && (err != KErrCorrupt) && (err != KErrDiskFull))
        {
        // leave to signal the caller that there was an error why creating and opening
        // one or more of the databases
        User::Leave(err);
        }
    else if (err == KErrDiskFull)
        {
        iOutOfDisk = ETrue;
        }
    else
        {
        // do nothing
        }

    // Verify the volume ids of each drive matches the database
    MPX_TRAP(err,VerifyVolumeIdL());
    if ((err != KErrNone) && (err != KErrDiskFull))
        {
        // leave to signal the caller that there was an error why creating and opening
        // one or more of the databases
        User::Leave(err);
        }
    else if (err == KErrDiskFull)
        {
        iOutOfDisk = ETrue;
        }

//#ifdef _DEBUG
//    iDbManager->PrintDatabaseL();    // PREQ2536 the files sqlrowsetutil.h and sqlrowsetutil.cpp has been removed
//#endif

    MPX_DEBUG2("CMPXDbHandler::ConstructL DbCount[%d]", iDbManager->DatabaseCount());
    }

// ----------------------------------------------------------------------------
// Add song to collection
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::AddSongL(
    const CMPXMedia& aMedia,
    CMPXMessageArray* aMessageArray)
    {
    MPX_FUNC("CMPXDbHandler::AddSongL");

    BeginTransactionL();
    TUint32 songId(0);
    MPX_TRAPD(err, songId = DoAddSongL(aMedia,aMessageArray));

    if (iOutOfDisk && (err == KErrNotFound))
        {
        err = KErrDiskFull;
        }
    EndTransactionL(err);

    return songId;
    }

// ----------------------------------------------------------------------------
// Add song to collection with no database transaction
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::AddSongWithNoTransactionL(
    const CMPXMedia& aMedia,
    CMPXMessageArray* aMessageArray)
    {
    MPX_FUNC("CMPXDbHandler::AddSongWithNoTransactionL");

    TUint32 songId(0);
    MPX_TRAPD(err, songId = DoAddSongL(aMedia,aMessageArray));

    if (iOutOfDisk && (err == KErrNotFound))
        {
        err = KErrDiskFull;
        }
    User::LeaveIfError(err);

    return songId;
    }

// ----------------------------------------------------------------------------
// Add playlist to collection
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::AddPlaylistL(
    const CMPXMedia& aMedia)
    {
    MPX_FUNC("CMPXDbHandler::AddPlaylistL");

    BeginTransactionL();
    TUint32 playlistId(0);
    MPX_TRAPD(err, playlistId = DoAddPlaylistL(aMedia));
    EndTransactionL(err);

    return playlistId;
    }

// ----------------------------------------------------------------------------
// Add song to playlist
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::AddSongToPlaylistL(
    const CMPXMedia& aMedia)
    {
    MPX_FUNC("CMPXDbHandler::AddSongToPlaylistL");

    BeginTransactionL();
    TUint32 playlistId(0);
    MPX_TRAPD(err, playlistId = DoAddSongToPlaylistL(aMedia));
    EndTransactionL(err);

    return playlistId;
    }

#ifdef ABSTRACTAUDIOALBUM_INCLUDED
// ----------------------------------------------------------------------------
// Add AbstractAlbum to collection
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::AddAbstractAlbumL(
    const CMPXMedia& aMedia,
    CMPXMessageArray* aMessageArray)
    {
    MPX_FUNC("CMPXDbHandler::AddAbstractAlbumL");

    BeginTransactionL();
    TBool newRecord(EFalse);
    TInt err(KErrNone);

    TDriveUnit drive(aMedia.ValueText(KMPXMediaGeneralUri));
    TPtrC uri(aMedia.ValueText(KMPXMediaGeneralUri).Left(KMCMaxTextLen));
    TPtrC name(aMedia.ValueText(KMPXMediaGeneralTitle).Left(KMCMaxTextLen));
    TPtrC albumartist(aMedia.ValueText(KMPXMediaMusicAlbumArtist).Left(KMCMaxTextLen));

    //only insert to AbstractAlbum table when it is new item
    TUint32 abstractAlbumId(MPXDbCommonUtil::GenerateUniqueIdL(iFs, EMPXAbstractAlbum, uri, EFalse));

    newRecord = !iDbAbstractAlbum->CategoryItemExistsL(drive, abstractAlbumId);

    if (newRecord)
        {
        MPX_TRAP(err, abstractAlbumId = iDbAbstractAlbum->AddItemL(uri, name, albumartist, drive, newRecord, EFalse));
        if (iOutOfDisk && (err == KErrNotFound))
            {
            err = KErrDiskFull;
            }
        if (aMessageArray)
            {
            MPXDbCommonUtil::AddItemChangedMessageL(*aMessageArray, abstractAlbumId, EMPXItemInserted,
            EMPXAbstractAlbum, KDBPluginUid);
            }
        }
    EndTransactionL(err);

    return abstractAlbumId;
    }

// ----------------------------------------------------------------------------
// Update abstractalbum info to AbstractAlbum table and all songs which associate 
// with AbstractAlbum in the collection
// ----------------------------------------------------------------------------
//
CMPXDbActiveTask::TChangeVisibility CMPXDbHandler::UpdateAbstractAlbumL(
    const CMPXMedia& aMedia,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::UpdateAbstractAlbumL");

    CMPXDbActiveTask::TChangeVisibility visibleChange(CMPXDbActiveTask::ENotVisibile);
    TUint32 itemId(0);
    if (aMedia.IsSupported(KMPXMediaGeneralUri))
        {
        const TDesC& uri(aMedia.ValueText (KMPXMediaGeneralUri));
        TDriveUnit drive(aMedia.ValueText(KMPXMediaGeneralUri));

        //get Id based on new uri
        itemId = MPXDbCommonUtil::GenerateUniqueIdL(iFs, EMPXAbstractAlbum, uri, EFalse);
        if (!itemId)
            {
            User::Leave(KErrNotSupported);
            }

        //if updating to new item, need do renaming for AbstractAlbum: delete old one and insert a new entry to AbstractAlbum table
        //if a new item, need update songs associated and renaming albstractalbum table
        if (aMedia.IsSupported(KMPXMediaGeneralId))
            {
            MPX_DEBUG1("CMPXDbHandler::UpdateAbstractAlbumL, rename case");
            
            BeginTransactionL();
            //get old id, for renaming
            TInt err(KErrNone);
            TUint32 oldId = (aMedia.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId)).iId2;
            if (oldId && (oldId != itemId) )  //pass the old UID for renaming, do not care if new itemId exist or not
                {
                //get old Uri based on old Id
                HBufC* oldUri = iDbAbstractAlbum->GetUriL(oldId);
                CleanupStack::PushL(oldUri);

                //add and update new item to AbstractAlbum table
                //old existing item field values need to be saved and added when adding new item                        
                MPX_TRAP(err, itemId = iDbAbstractAlbum->AddUpdatedItemL(oldId, uri ));

                if (iOutOfDisk && (err == KErrNotFound))
                    {
                    err = KErrDiskFull;
                    }

                MPXDbCommonUtil::AddItemChangedMessageL(aItemChangedMessages, itemId, EMPXItemInserted,
                EMPXAbstractAlbum, KDBPluginUid);

                //find and update all songs associated with oldId
                RArray<TMPXAttribute> songAttributes;
                CleanupClosePushL(songAttributes);
                songAttributes.AppendL(KMPXMediaGeneralId);

                CMPXMediaArray* mediaArray = CMPXMediaArray::NewL();
                CleanupStack::PushL(mediaArray);

                //get all songs associated
                MPX_TRAP(err, iDbMusic->GetAllSongsForAbstractAlbumL(oldId, songAttributes.Array(), *mediaArray));
                if (err == KErrNotFound)
                    {
                    // Leave with KErrNotFound
                    MPX_DEBUG1("CMPXDbHandler::UpdateAbstractAlbumL, leave with not found");
                    User::Leave(KErrNotFound);
                    }

                TInt count(mediaArray->Count());
                MPX_DEBUG2("CMPXDbHandler::UpdateAbstractAlbumL, [%d] songs associated", count);
                //go through all songs for updating
                for (TInt i = 0; i < count; i++)
                    {
                    CMPXMedia* song = mediaArray->AtL(i);
                    song->SetTextValueL(KMPXMediaMusicAlbumArtFileName, uri );
                    visibleChange = UpdateSongL(*song, aItemChangedMessages);
                    }
                CleanupStack::PopAndDestroy(mediaArray);
                CleanupStack::PopAndDestroy(&songAttributes);

                //rename TN
                iDbAbstractAlbum->HandleTNL(*oldUri, uri, 0);
                CleanupStack::PopAndDestroy(oldUri);
                }
            EndTransactionL(err);
            }//renaming

        //check if abstractAlbum exist in ABSTRACTALBUM table before update it
        else if (iDbAbstractAlbum->CategoryItemExistsL(drive, itemId))
            {
            //support updating Name, AlbumArtist fields for AbstractAlbum table
            iDbAbstractAlbum->UpdateItemL(itemId, aMedia, drive, &aItemChangedMessages);
            }
        }
    return visibleChange;
    }

// ----------------------------------------------------------------------------
// Update all songs which associate with AbstractAlbum to new AbstractAlbum info 
// in the collection
// ----------------------------------------------------------------------------
//
CMPXDbActiveTask::TChangeVisibility CMPXDbHandler::UpdateSongsAbstractAlbumInfoL(
    const CMPXMedia& aMedia,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::UpdateSongsAbstractAlbumInfoL");
    const TDesC& uri(aMedia.ValueText (KMPXMediaGeneralUri));
    //need to update songs information to music table
    CMPXMediaArray* mediaArray = aMedia.Value<CMPXMediaArray>(KMPXMediaArrayContents);
    User::LeaveIfNull(mediaArray);
    TInt count(mediaArray->Count());
    CMPXDbActiveTask::TChangeVisibility visibleChange(CMPXDbActiveTask::ENotVisibile);
    for (TInt i = 0; i < count; i++)
        {
        CMPXMedia* mediaSong = mediaArray->AtL(i);
        mediaSong->SetTextValueL(KMPXMediaMusicAlbumArtFileName, uri );
        visibleChange = UpdateSongL(*mediaSong, aItemChangedMessages);
        }
    return visibleChange;
    }
#endif // ABSTRACTAUDIOALBUM_INCLUDED

// ----------------------------------------------------------------------------
// Update a song in the collection
// ----------------------------------------------------------------------------
//
CMPXDbActiveTask::TChangeVisibility CMPXDbHandler::UpdateSongL(
    const CMPXMedia& aMedia,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::UpdateSongL");

    BeginTransactionL();
    CMPXDbActiveTask::TChangeVisibility visibleChange(CMPXDbActiveTask::ENotVisibile);
    MPX_TRAPD(err, visibleChange = DoUpdateSongL(aMedia, aItemChangedMessages));
    EndTransactionL(err);
    return visibleChange;
    }

// ----------------------------------------------------------------------------
// Update a playlist in the collection
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::UpdatePlaylistL(
    const CMPXMedia& aMedia,
    CMPXMessageArray& aMessageArray)
    {
    MPX_FUNC("CMPXDbHandler::UpdatePlaylistL");

    BeginTransactionL();
    MPX_TRAPD(err, DoUpdatePlaylistL(aMedia, aMessageArray));
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
// Updates the playlist songs
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::UpdatePlaylistSongsL(
    const CMPXMedia& aMedia,
    CMPXMessage& aMessage)
    {
    MPX_FUNC("CMPXDbHandler::UpdatePlaylistSongsL");

    BeginTransactionL();
    MPX_TRAPD(err, DoUpdatePlaylistSongsL(aMedia, aMessage));
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
// Reorder a song in a playlist
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::ReorderPlaylistL(
    const TMPXItemId& aPlaylistId,
    const TMPXItemId& aSongId,
    TUint aOriginalOrdinal,
    TUint aNewOrdinal,
    CMPXMessage& aMessage)
    {
    MPX_DEBUG5("-->CMPXDbHandler::ReorderPlaylistL(0x%x, 0x%x, %d, %d)",
               aPlaylistId.iId2, aSongId.iId2, aOriginalOrdinal, aNewOrdinal);

    BeginTransactionL();
    MPX_TRAPD(err, DoReorderPlaylistL(aPlaylistId, aSongId, aOriginalOrdinal, aNewOrdinal, aMessage));
    EndTransactionL(err);
    MPX_DEBUG2("<--CMPXDbHandler::ReorderPlaylistL() error=%d", err);
    }

// ----------------------------------------------------------------------------
// Remove the entire music collection database
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RemoveEntireCollectionL()
    {
    MPX_FUNC("CMPXDbHandler::RemoveEntireCollectionL");
    BeginTransactionL();
    MPX_TRAPD(err, iDbManager->RecreateAllDatabasesL());
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
// Delete a song from collection
// The function notifies collection model to perform deletion
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RemoveSongL(
    TUint32 aSongId,
    CDesCArray& aUriArray,
    CMPXMessageArray& aItemChangedMessages,
    TBool aDeleteRecord)
    {
    MPX_FUNC("CMPXDbHandler::RemoveSongL");

    MPX_TRAPD(err, DoRemoveSongL(aSongId, aUriArray, aItemChangedMessages, aDeleteRecord));

    MPX_TRAP(err, DoRemoveSongFromPlaylistL(aSongId,aItemChangedMessages));

    }

// ----------------------------------------------------------------------------
// Removes a category of songs from the music collection,
// and its corresponding category in the lookup table
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RemoveSongsMatchingCategoryL(
    TMPXGeneralCategory aCategory,
    TUint32 aCategoryId,
    CDesCArray& aUriArray,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::RemoveSongsMatchingCategoryL");

    BeginTransactionL();
    MPX_TRAPD(err, DoRemoveSongsMatchingCategoryL(aCategory, aCategoryId, aUriArray, aItemChangedMessages));
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------------------------------------
// Delete songs for the specified artist and album from collection
// The function notifies collection model to perform deletion
// ----------------------------------------------------------------------------------------------------------
//
void CMPXDbHandler::RemoveSongsMatchingArtistAndAlbumL(
    TUint32 aArtistId,
    TUint32 aAlbumId,
    CDesCArray& aUriArray,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::RemoveSongsMatchingArtistAndAlbumL");

    BeginTransactionL();
    MPX_TRAPD(err, DoRemoveSongsMatchingArtistAndAlbumL(aArtistId, aAlbumId, aUriArray,
        aItemChangedMessages));
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
// Remove all playlists from collection
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RemoveAllPlaylistsL()
    {
    MPX_FUNC("CMPXDbHandler::RemoveAllPlaylistsL");

    BeginTransactionL();
    MPX_TRAPD(err, DoRemoveAllPlaylistsL());
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
// Remove specified playlist
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RemovePlaylistL(
    TUint32 aPlaylistId,
    CDesCArray& aUriArray,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::RemovePlaylistL");

    BeginTransactionL();
    MPX_TRAPD(err, DoRemovePlaylistL(aPlaylistId, aUriArray, aItemChangedMessages));
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
// Remove song from playlist songs table
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RemoveSongFromPlaylistL(
    TUint32 aPlaylistId,
    const TMPXItemId& aSongId,
    TInt aOrdinal,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::RemoveSongFromPlaylistL");
    MPX_DEBUG5("CMPXDbHandler::RemoveSongFromPlaylistL(playlist 0x%x, songId [0x%x,0x%x], ordinal %d)",
        aPlaylistId, aSongId.iId1, aSongId.iId2, aOrdinal);

    MPX_TRAPD(err, DoRemoveSongFromPlaylistL(aPlaylistId, aSongId, aOrdinal, aItemChangedMessages));
    if ( err && InTransaction() )
        {
        // only end transaction if there's an error
        EndTransactionL( err );
        }
    }

#ifdef ABSTRACTAUDIOALBUM_INCLUDED
// ----------------------------------------------------------------------------
// Remove specified abstractalbum
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RemoveAbstractAlbumL(
    TUint32 aAbstractAlbumId,
    CMPXMessageArray& aItemChangedMessages, TBool aFileDeleted)
    {
    MPX_FUNC("CMPXDbHandler::RemoveAbstractAlbumL");
    iDbAbstractAlbum->RemoveAbstractAlbumL(aAbstractAlbumId, aItemChangedMessages, aFileDeleted);
    }
#endif // ABSTRACTAUDIOALBUM_INCLUDED

// ----------------------------------------------------------------------------
// Cleanup records marked as deleted. This is designated for MTP to clean up records marked as deleted
// at the end of its session.
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::CleanupDeletedRecordsL()
    {
    MPX_FUNC("CMPXDbHandler::CleanupDeletedRecordsL");

    BeginTransactionL();
    MPX_TRAPD(err, DoCleanupDeletedRecordsL());
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
//  Read all songs and cache them into an array ordered by song name
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAllSongsL(
    CMPXMediaArray* aMediaArray,
    const TArray<TMPXAttribute>& aAttrs)
    {
    MPX_FUNC("CMPXDbHandler::GetAllSongsL");
    iDbMusic->GetAllSongsL(aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
//  Read a limited # of songs and cache them into an array ordered by song name
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAllSongsLimitedL(const TArray<TMPXAttribute>& aAttrs,
                                        CMPXMediaArray& aMediaArray, TInt aLimit)
    {
    MPX_FUNC("CMPXDbHandler::GetAllSongsLimitedL");
    iDbMusic->GetAllSongsLimitedL( aAttrs, aMediaArray, aLimit );
    }

// ----------------------------------------------------------------------------
//  Read all songs and cache them into an array ordered by song name
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongsInBlockL(
    CMPXMediaArray* aMediaArray,
    const TArray<TMPXAttribute>& aAttrs,
    TPtrC aTitle,
    TUint aNumOfSongs,
    TBool aAsc)
    {
    MPX_FUNC("CMPXDbHandler::GetSongsInBlockL");
    iDbMusic->GetSongsInBlockL(aAttrs, *aMediaArray, aTitle, aNumOfSongs, aAsc);
    }

// ----------------------------------------------------------------------------
//  Read songs at a particular offset
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongsAtOffsetL( CMPXMediaArray* aMediaArray,
                                       const TArray<TMPXAttribute>& aAttrs,
                                       TInt aOffset,
                                       TInt aCount )
    {
    MPX_DEBUG1("CMPXDbHandler::GetSongsAtOffsetL <--");
    iDbMusic->GetSongsAtOffsetL( *aMediaArray, aAttrs, aOffset, aCount );
    }

// ----------------------------------------------------------------------------
// Get all songs matching the given artist ID
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongsMatchingArtistL(
    TUint aArtistId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetSongsMatchingArtistL");
    iDbMusic->GetSongsForArtistL(aArtistId, aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get all songs matching the given album ID
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongsMatchingAlbumL(
    TUint aAlbumId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetSongsMatchingAlbumL");
    iDbMusic->GetSongsForAlbumL(aAlbumId, aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------------------------------------
// Get all songs matching the given artist and album ID
// ----------------------------------------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongsMatchingArtistAndAlbumL(
    TUint aArtistId,
    TUint aAlbumId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetSongsMatchingArtistAndAlbumL");
    iDbMusic->GetSongsForArtistAndAlbumL(aArtistId, aAlbumId, aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get all songs matching the given genre ID
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongsMatchingGenreL(
    TUint aGenreId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetSongsMatchingGenreL");
    iDbMusic->GetSongsForGenreL(aGenreId, aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get all songs matching the given composer ID
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongsMatchingComposerL(
    TUint aComposerId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetSongsMatchingComposerL");
    iDbMusic->GetSongsForComposerL(aComposerId, aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get all songs that belong to the given playlist
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongsMatchingPlaylistL(
    TUint aPlaylistId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetSongsMatchingPlaylistL");
    GetPlaylistSongsL(aPlaylistId, aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get song matching the given song ID
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongL(
    TUint32 aSongId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMedia& aMedia)
    {
    MPX_FUNC("CMPXDbHandler::GetSongL");
    iDbMusic->GetSongL(aSongId, aAttrs, aMedia);
    }

// ----------------------------------------------------------------------------
// GetSongL
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetSongL(
    TUint32 aSongId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray& aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetSongL");

    CMPXMedia* media = CMPXMedia::NewL();
    CleanupStack::PushL(media);

    GetSongL(aSongId, aAttrs, *media);
    aMediaArray.AppendL(*media);

    CleanupStack::PopAndDestroy(media);
    }

// ----------------------------------------------------------------------------
// Get song matching the given song ID and playlist ID
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetPlaylistSongL(
    TUint32 aSongId,
    TUint32 aPlaylistId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMedia& aMedia)
    {
    MPX_DEBUG3("-->CMPXDbHandler::GetPlaylistSongL songId[0x%x] playlistId[0x%x]", aSongId, aPlaylistId);

    // complete the song information from the Music table
    MPX_TRAPD(err, iDbMusic->GetSongL(aSongId, aAttrs, aMedia));

    //
    // song not found in Music table
    //
    if (err == KErrNotFound)
        {
        //
        // Leave with KErrNotFound if one of the following is true:
        // 1) the requested song is in an auto playlist. Since auto-playlist isn't
        //    stored in playlist tables, we won't be able to retrieve info elsewhere
        // 2) the requested song is in a user playlist but we cannot find the song
        //    info from playlist tables either
        //
        if (EMPXNoAutoPlaylist != iAutoPlaylist->AutoPlaylistTypeL(aPlaylistId) ||
            !iDbPlaylist->Songs().GetSongL(aPlaylistId, aSongId, aAttrs, aMedia))
            {
            User::Leave(KErrNotFound);
            }
        }

    //
    // Unable to read information from Music table
    //
    else
        {
        // ignore the error if KErrNotFound
        User::LeaveIfError(err);
        }
    MPX_DEBUG1("<--CMPXDbHandler::GetPlaylistSongL");
    }

// ----------------------------------------------------------------------------
// GetPlaylistSongL
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetPlaylistSongL(
    TUint32 aSongId,
    TUint32 aPlaylistId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray& aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetPlaylistSongL");

    CMPXMedia* media = CMPXMedia::NewL();
    CleanupStack::PushL(media);

    GetPlaylistSongL(aSongId, aPlaylistId, aAttrs, *media);
    aMediaArray.AppendL(*media);

    CleanupStack::PopAndDestroy(media);
    }

// ----------------------------------------------------------------------------
// Get song matching the given URI
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::GetSongIdMatchingUriL(
    const TDesC& aUri)
    {
    MPX_FUNC("CMPXDbHandler::GetSongIdMatchingUriL");
    return MPXDbCommonUtil::GenerateUniqueIdL(iFs, EMPXCollection, aUri, EFalse);
    }

#ifdef ABSTRACTAUDIOALBUM_INCLUDED
// ----------------------------------------------------------------------------
// Get abstractalbum Id matching the given URI
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::GetAbstractAlbumIdMatchingUriL(
    const TDesC& aUri)
    {
    MPX_FUNC("CMPXDbHandler::GetAbstractAlbumIdMatchingUriL");
    return MPXDbCommonUtil::GenerateUniqueIdL(iFs, EMPXAbstractAlbum, aUri, EFalse);
    }
#endif // ABSTRACTAUDIOALBUM_INCLUDED

// ----------------------------------------------------------------------------
// Get all artists
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAllArtistsL(
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetAllArtistsL");
    iDbArtist->GetAllCategoryItemsL(aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get all albums
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAllAlbumsL(
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetAllAlbumsL");
    iDbAlbum->GetAllCategoryItemsL(aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get all albums for the given artist ID
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAlbumsMatchingArtistL(
    TUint aArtistId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray& aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetAlbumsMatchingArtistL");
    RArray<TMPXAttribute> attributes;
    CleanupClosePushL( attributes );

    TBool tryGetSongCount = EFalse;
    TInt attrCount(aAttrs.Count());
    TInt i = 0;
    for (i = 0; i < attrCount; i++)
        {
        TInt contentId(aAttrs[i].ContentId());
        TUint attributeId(aAttrs[i].AttributeId());

        if (contentId == KMPXMediaIdGeneral && attributeId & EMPXMediaGeneralCount)
            {
            MPX_DEBUG1("    EMPXMediaGeneralCount");

            attributes.Append(TMPXAttribute(KMPXMediaIdGeneral, attributeId & ~EMPXMediaGeneralCount));

            tryGetSongCount = ETrue;
            break;
            }

        attributes.Append(aAttrs[i]);
        }

    for (TInt j = i+1; j < attrCount; j++)
        {
        attributes.Append(aAttrs[j]);
        }
    iDbAlbum->GetSubCategoryItemsL(EMPXArtist, aArtistId, attributes.Array(), aMediaArray);
    CleanupStack::PopAndDestroy(&attributes);

    TInt pPath(0);
    if (aMediaArray.Count())
        {
        CMPXMedia* pMedia = aMediaArray[0];
        if (pMedia->IsSupported(KMPXMediaGeneralValue))
            {
            pPath = pMedia->ValueTObjectL<TInt>(KMPXMediaGeneralValue);
            MPX_ASSERT(pPath);
            }
        }

    TInt albumCount(aMediaArray.Count());
    if (albumCount)
        {
        if ( tryGetSongCount )
            {
            TInt startIndex = pPath ? 1 : 0;

            for (TInt i = startIndex; i < albumCount; i++)
                {
                CMPXMedia* media = aMediaArray[i];
                TUint32 albumId((media->ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId)));

                TInt songCount = iDbAlbum->GetSongsCountInAlbumMatchingArtistL(aArtistId, albumId);

                media->SetTObjectValueL<TInt>(KMPXMediaGeneralCount, songCount );
                MPX_DEBUG2("    SongCount[%d]", songCount );
                }
            }
        }
    }

// ----------------------------------------------------------------------------
// Get all genres
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAllGenresL(
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetAllGenresL");
    iDbGenre->GetAllCategoryItemsL(aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get all composers
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAllComposersL(
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetAllComposersL");
    iDbComposer->GetAllCategoryItemsL(aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get the playlist ID of the playlist that matches the given URI
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::GetPlaylistIdMatchingUriL(
    const TDesC& aUri)
    {
    MPX_FUNC("CMPXDbHandler::GetPlaylistIdMatchingUriL");
    return iDbPlaylist->GetIdL(aUri);
    }

// ----------------------------------------------------------------------------
// CMPXDbHandler::IsAutoPlaylistL
// ----------------------------------------------------------------------------
//
TBool CMPXDbHandler::IsAutoPlaylistL(
    TUint32 aPlaylistId)
    {
    MPX_FUNC("CMPXDbHandler::IsAutoPlaylistL");
    return (iAutoPlaylist->AutoPlaylistTypeL(aPlaylistId) != EMPXNoAutoPlaylist);
    }

// ----------------------------------------------------------------------------
// Get all user playlist names
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAllPlaylistsL(
    CMPXMediaArray* aMediaArray,
    const TArray<TMPXAttribute>& aAttrs)
    {
    MPX_FUNC("CMPXDbHandler::GetAllPlaylistsL");
    iDbPlaylist->GetAllPlaylistsL(aAttrs, *aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get all system playlist names
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetAllSystemPlaylistNamesL(
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetAllSystemPlaylistNamesL()");
    iAutoPlaylist->GetAllPlaylistsL(*aMediaArray);
    }

// ----------------------------------------------------------------------------
// Get the name of the row matching the given ID
// ----------------------------------------------------------------------------
//
HBufC* CMPXDbHandler::GetNameMatchingIdL(
    const TUint32 aId) const
    {
    MPX_FUNC("CMPXDbHandler::GetNameMatchingIdL()");

    HBufC* name(NULL);
    TMPXGeneralCategory category(MPX_ITEM_CATEGORY(aId));
    switch (category)
        {
        case EMPXCollection:
            {
            // song name
            name = iDbMusic->GetNameL(aId);
            break;
            }
        case EMPXPlaylist:
            {
            // playlist name
            if (iAutoPlaylist->AutoPlaylistTypeL(aId) != EMPXNoAutoPlaylist)
                {
                name = iAutoPlaylist->AutoPlaylistNameL(aId).AllocL();
                }
            else
                {
                name = iDbPlaylist->GetNameL(aId);
                }
            break;
            }
        default:
            {
            // category name
            name = DbCategoryL(category)->GetNameL(aId);
            break;
            }
        }

    return name;
    }

// ----------------------------------------------------------------------------
// Get the URI of the row matching the given ID
// ----------------------------------------------------------------------------
//
HBufC* CMPXDbHandler::GetUriMatchingIdL(
    const TUint32 aId) const
    {
    MPX_FUNC("CMPXDbHandler::GetUriMatchingIdL");

    HBufC* uri(NULL);
    switch (MPX_ITEM_CATEGORY(aId))
        {
        case EMPXCollection:
            {
            // song URI
            uri = iDbMusic->GetUriL(aId);
            break;
            }
        case EMPXPlaylist:
            {
            // playlist URI
            uri = iDbPlaylist->GetUriL(aId);
            break;
            }
        default:
            User::Leave(KErrNotSupported);
        }

    return uri;
    }

// ----------------------------------------------------------------------------
// Get category
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetCategoryL(
    const TUint32 aCategoryId,
    TMPXGeneralCategory aCategory,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMedia* aMedia)
    {
    MPX_FUNC("CMPXDbHandler::GetCategoryL");

    switch (aCategory)
        {
        case EMPXPlaylist:
            {
            if (iAutoPlaylist->AutoPlaylistTypeL(aCategoryId) != EMPXNoAutoPlaylist)
                {
                iAutoPlaylist->GetPlaylistL(aCategoryId, aAttrs, *aMedia);
                }
            else
                {
                iDbPlaylist->GetPlaylistL(aCategoryId, aAttrs, *aMedia);
                }
            break;
            }
        default:
            {
            DbCategoryL(aCategory)->GetCategoryItemL(aCategoryId, aAttrs, *aMedia);
            break;
            }
        }
    }

// ----------------------------------------------------------------------------
//  Get the duration for all songs
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::GetAllSongsDurationL()
    {
    MPX_FUNC("CMPXDbHandler::GetAllSongsDurationL");
    return iDbMusic->AllSongsDurationL();
    }

// ----------------------------------------------------------------------------
//  Get the duration for an artist
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::GetArtistDurationL(
    TInt aArtistId)
    {
    MPX_FUNC("CMPXDbHandler::GetArtistDurationL");
    return iDbMusic->ArtistDurationL(aArtistId);
    }

// ----------------------------------------------------------------------------
//  Get the duration for an album
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::GetAlbumDurationL(
    TInt aAlbumId)
    {
    MPX_FUNC("CMPXDbHandler::GetAlbumDurationL");
    return iDbMusic->AlbumDurationL(aAlbumId);
    }

// ----------------------------------------------------------------------------
//  Get the duration for an artist/album combination
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::GetArtistAlbumDurationL(
    TInt aArtistId,
    TInt aAlbumId)
    {
    MPX_FUNC("CMPXDbHandler::GetArtistAlbumDurationL");
    return iDbMusic->ArtistAlbumDurationL(aArtistId, aAlbumId);
    }

// ----------------------------------------------------------------------------
//  Get the duration for a composer
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::GetComposerDurationL(
    TInt aComposerId)
    {
    MPX_FUNC("CMPXDbHandler::GetComposerDurationL");
    return iDbMusic->ComposerDurationL(aComposerId);
    }

// ----------------------------------------------------------------------------
//  Get the duration for a genre
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::GetGenreDurationL(
    TInt aGenreId)
    {
    MPX_FUNC("CMPXDbHandler::GetGenreDurationL");
    return iDbMusic->GenreDurationL(aGenreId);
    }

// ----------------------------------------------------------------------------
//  Get the duration for a user playlist
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::GetUserPlaylistDurationL(
    TInt aPlaylistId)
    {
    MPX_FUNC("CMPXDbHandler::GetUserPlaylistDurationL");

    RArray<TMPXAttribute> attributes;
    CleanupClosePushL(attributes);
    attributes.AppendL(KMPXMediaGeneralId);
    attributes.AppendL(TMPXAttribute(KMPXMediaIdGeneral, EMPXMediaGeneralDuration));

    CMPXMediaArray* mediaArray = CMPXMediaArray::NewL();
    CleanupStack::PushL(mediaArray);

    GetPlaylistSongsL(aPlaylistId, attributes.Array(), *mediaArray);

    TInt duration(0);
    TInt count(mediaArray->Count());
    for (TInt index = 0; index < count; ++index)
        {
        CMPXMedia* media((*mediaArray)[index]);
        if (media->IsSupported(KMPXMediaGeneralDuration))
            {
            duration += media->ValueTObjectL<TInt>(KMPXMediaGeneralDuration);
            }
        }

    CleanupStack::PopAndDestroy(mediaArray);
    CleanupStack::PopAndDestroy(&attributes);

    return duration;
    }

// ----------------------------------------------------------------------------
//  Get the duration for a playlist
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::GetPlaylistDurationL(
    TInt aPlaylistId)
    {
    MPX_FUNC("CMPXDbHandler::GetPlaylistDurationL");

    TInt duration(0);

    if (aPlaylistId == iAutoPlaylist->AutoPlaylistIdL(EMPXRecentlyPlayedPlaylist))
        {
        duration = iDbMusic->RecentlyPlayedDurationL();
        }
    else if (aPlaylistId == iAutoPlaylist->AutoPlaylistIdL(EMPXMostPlayedPlaylist))
        {
        duration = iDbMusic->MostPlayedDurationL();
        }
    else if (aPlaylistId == iAutoPlaylist->AutoPlaylistIdL(EMPXRecentlyAddedPlaylist))
        {
        duration = iDbMusic->RecentlyAddedDurationL();
        }
    else
        {
        duration = GetUserPlaylistDurationL(aPlaylistId);
        }

    return duration;
    }

// ----------------------------------------------------------------------------
// Find the number of items of a specified type
// ----------------------------------------------------------------------------
//
TInt CMPXDbHandler::NumberOfItemsL(
    TMPXGeneralCategory aCategory)
    {
    MPX_FUNC("CMPXDbHandler::NumberOfItemsL");

    TInt count(0);
    switch(aCategory)
        {
        case EMPXSong:
            {
            count = iDbMusic->CountL();
            break;
            }
        case EMPXPlaylist:
            {
            // return the total number of playlists, including the system ones
            count = iDbPlaylist->CountL() + EMPXAutoPlaylistCount;
            break;
            }
        default:
            {
            count = DbCategoryL(aCategory)->CountL();
            break;
            }
        }

    return count;
    }

// ----------------------------------------------------------------------------
// CMPXDbHandler::FindAllLC
// ----------------------------------------------------------------------------
//
CMPXMedia* CMPXDbHandler::FindAllLC(
    const CMPXMedia& aCriteria,
    const TArray<TMPXAttribute>& aAttrs)
    {
    MPX_FUNC("CMPXDbHandler::FindAllLC");

    // leave if the given media doesn't contain the following attributes
    if (!aCriteria.IsSupported(KMPXMediaGeneralType) ||
        !aCriteria.IsSupported(KMPXMediaGeneralCategory))
        {
        User::Leave(KErrArgument);
        }

    RArray<TInt> supportedIds;
    CleanupClosePushL(supportedIds);
    supportedIds.AppendL(KMPXMediaIdContainer);
    MPXDbCommonUtil::FillInSupportedUIDsL(aAttrs, supportedIds);

    CMPXMedia* entries = CMPXMedia::NewL(supportedIds.Array());
    CleanupStack::PopAndDestroy(&supportedIds);
    CleanupStack::PushL(entries);

    CMPXMediaArray* array = CMPXMediaArray::NewL();
    CleanupStack::PushL(array);

    FindAllL(aCriteria, aAttrs, array);

    entries->SetTObjectValueL<TMPXGeneralType>(KMPXMediaGeneralType, EMPXGroup);
    entries->SetTObjectValueL<TMPXGeneralCategory>(KMPXMediaGeneralCategory,
        aCriteria.ValueTObjectL<TMPXGeneralCategory>(KMPXMediaGeneralCategory));
    entries->SetCObjectValueL(KMPXMediaArrayContents, array);
    entries->SetTObjectValueL<TInt>(KMPXMediaArrayCount, array->Count());

    CleanupStack::PopAndDestroy(array);
    return entries;
    }

// ----------------------------------------------------------------------------
// Set the last refreshed time into the collection
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::SetLastRefreshedTimeL(
    TTime aTime)
    {
    MPX_FUNC("CMPXDbHandler::SetLastRefreshedTimeL");

    BeginTransactionL();
    MPX_TRAPD(err, iDbAuxiliary->SetLastRefreshedTimeL(aTime));
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
// Get the last refreshed time from the collection
// ----------------------------------------------------------------------------
//
TTime CMPXDbHandler::GetLastRefreshedTimeL()
    {
    MPX_FUNC("CMPXDbHandler::GetLastRefreshedTimeL");
    return iDbAuxiliary->LastRefreshedTimeL();
    }

// ----------------------------------------------------------------------------
// Set the db corrupted state for all drives
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::SetDBCorruptedL(
    TBool aCorrupted)
    {
    MPX_FUNC("CMPXDbHandler::SetDBCorruptedL");

    BeginTransactionL();
    MPX_TRAPD(err, iDbAuxiliary->SetDBCorruptedL(aCorrupted));
    EndTransactionL(err);
    }

// ----------------------------------------------------------------------------
// Gets the db corrupted state for all drives
// ----------------------------------------------------------------------------
//
TBool CMPXDbHandler::IsDBCorruptedL()
    {
    MPX_FUNC("CMPXDbHandler::IsDBCorruptedL");
    return iDbAuxiliary->DBCorruptedL();
    }

// ----------------------------------------------------------------------------
// Have the databases been created?
// ----------------------------------------------------------------------------
//
TBool CMPXDbHandler::DatabaseCreated()
    {
    MPX_FUNC("CMPXDbHandler::DatabaseCreatedL");

    TBool AuxilaryDbIsRefreshed(EFalse);
    TRAP_IGNORE(AuxilaryDbIsRefreshed = iDbAuxiliary->IsRefreshedL());
    // If none of the databases were available, ie out of disk we return EFalse
    return iDbManager->IsInitialized() && AuxilaryDbIsRefreshed;
    }

// ----------------------------------------------------------------------------
// Open a database
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::OpenDatabaseL(
    TInt aDrive)
    {
    MPX_FUNC("CMPXDbHandler::OpenDatabaseL");
    MPX_DEBUG2( "CMPXDbHandler::OpenDatabaseL: %i", aDrive);
    iDbManager->OpenDatabaseL(aDrive);

    // Verify the volume ID after a remount event
    VerifyVolumeIdL( aDrive );
    }

// ----------------------------------------------------------------------------
// Close a database
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::CloseDatabaseL(
    TInt aDrive)
    {
    MPX_FUNC("CMPXDbHandler::CloseDatabaseL");
    MPX_DEBUG2( "CMPXDbHandler::CloseDatabaseL drive: %i", aDrive );
    iDbManager->CloseDatabaseL(aDrive);
    }

// ----------------------------------------------------------------------------
// Re-create all databases
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::ReCreateDatabasesL()
    {
    MPX_FUNC("CMPXDbHandler::ReCreateDatabasesL");
    iDbManager->RecreateAllDatabasesL();
    }

// ----------------------------------------------------------------------------
// Set handler refresh status
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RefreshStartL()
    {
    MPX_FUNC("CMPXDbHandler::RefreshStartL");

    iOutOfDisk = EFalse;
    // Re-open databases
    // This is needed for the case where we were OOD before, but user
    // has cleared some space but now try to refresh
    MPX_TRAPD(err, iDbManager->InitDatabasesL(iDbDrives));

    if(err == KErrDiskFull)
        {
            iOutOfDisk = ETrue;
        }

    User::LeaveIfError( err );
    	
    if(!iOutOfDisk)
    {
        MPX_TRAP(err,CheckDiskSpaceOnDrivesL());

        if(err == KErrDiskFull)
            {
            iOutOfDisk = ETrue;
            }
    }
#ifdef __RAMDISK_PERF_ENABLE
	iDbManager->CopyDBsToRamL();
#endif //__RAMDISK_PERF_ENABLE

    BeginTransactionL();
    iRefresh = ETrue;
    }

// ----------------------------------------------------------------------------
// Re-set handler refresh status
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::RefreshEndL()
    {
    MPX_FUNC("CMPXDbHandler::RefreshEndL");
    if ( iRefresh )
        { 
        iRefresh = EFalse;
        EndTransactionL(KErrNone);
        if (!iOutOfDisk)
            {
            // Write last refreshed time as current time
            // This also sets corrupt = 0
            TTime curTime;
            curTime.HomeTime();
            SetLastRefreshedTimeL(curTime);
            }

#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    //for AbstractAlbum garbage collection
    TRAP_IGNORE( iDbAbstractAlbum->AbstractAlbumCleanUpL() );
#endif
   
#ifdef __RAMDISK_PERF_ENABLE
        iDbManager->CopyDBsFromRamL();
#endif //__RAMDISK_PERF_ENABLE
        }
    }

// ----------------------------------------------------------------------------
// Notification of Mtp status change
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::MtpStartL()
    {
    MPX_DEBUG1("-->CMPXDbHandler::MtpStartL");
    if(!iMtpInUse)
        {
        iMtpInUse = ETrue;
        iOpOnDbCount = 0;
    
    #ifdef __RAMDISK_PERF_ENABLE
        TRAPD(err, iDbManager->CopyDBsToRamL(iMtpInUse));
        if ( err != KErrNone )
            {
            MPX_DEBUG2("CMPXDbHandler::MtpStartL error=%d", err);
            }
    #endif //__RAMDISK_PERF_ENABLE
    
        iDbManager->BeginL();
        }
//create Thumbnail manager session for cleanup for abstractalbum when MTP end.
//because when MTP end comes, in case of mode switching, we need to do it as fast as possible, 
//hence we don’t want the delay happens on that time.    
#ifdef RD_MPX_TNM_INTEGRATION           
    iDbAbstractAlbum->CreateTNMSessionL();
#endif  //RD_MPX_TNM_INTEGRATION
    MPX_DEBUG1("<--CMPXDbHandler::MtpStartL");
    }

// ----------------------------------------------------------------------------
// Notification of Mtp status change
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::MtpEndL()
    {
    MPX_DEBUG1("-->CMPXDbHandler::MtpEndL");
    iMtpInUse = EFalse;
    iOpOnDbCount = 0;
    iDbManager->CommitL();
    TInt err = KErrNone;


#ifdef __RAMDISK_PERF_ENABLE
    TRAP(err, iDbManager->CopyDBsFromRamL());
    if ( err != KErrNone )
        {
        MPX_DEBUG2("CMPXDbHandler::MtpEndL error=%d", err);
        }
#endif //__RAMDISK_PERF_ENABLE

#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    BeginTransactionL();
    TRAP(err, iDbAbstractAlbum->RemoveAbstractAlbumsWithNoSongL());
    if ( err != KErrNone )
        {
        MPX_DEBUG2("CMPXDbHandler::MtpEndL error happened when cleanup albstractalbum with no songs association[%d]", err);
        }
    EndTransactionL(err);
#endif
    MPX_DEBUG1("<--CMPXDbHandler::MtpEndL");
    }

// ----------------------------------------------------------------------------
// Get all records count for music
// ----------------------------------------------------------------------------
//
TUint CMPXDbHandler::GetMusicCountL(TInt aDrive)
    {
    MPX_FUNC("CMPXDbHandler::GetMusicCountL");
    TUint total(0);

    //music
    total += iDbMusic->GetDriveTrackCountL(aDrive);

    return total;
    }

// ----------------------------------------------------------------------------
// Get all records count for playlists
// ----------------------------------------------------------------------------
//
TUint CMPXDbHandler::GetPlaylistCountL(TInt aDrive)
    {
    MPX_FUNC("CMPXDbHandler::GetPlaylistCountL");
    TUint total(0);

    //playlist
    total += iDbPlaylist->GetDrivePlaylistCountL(aDrive);

    return total;
    }

// ----------------------------------------------------------------------------
// Get all records count for music and playlists
// ----------------------------------------------------------------------------
//
TUint CMPXDbHandler::GetTotalCountL(TInt aDrive)
    {
    MPX_FUNC("CMPXDbHandler::GetTotalCountL");
    TUint total(0);

    //music
    total += iDbMusic->GetDriveTrackCountL(aDrive);
    //playlist
    total += iDbPlaylist->GetDrivePlaylistCountL(aDrive);

    return total;
    }

// ----------------------------------------------------------------------------
// Get all records count for music and playlists
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetMusicUriArrayL(TInt aDrive, TInt aFromID, TInt aRecords,
                                      CDesCArray& aUriArr, TInt& aLastID)
    {
    MPX_FUNC("CMPXDbHandler::GetMusicUriArrayL");

    iDbMusic->GetMusicUriArrayL(aDrive,aFromID,aRecords,aUriArr,aLastID);
    }

// ----------------------------------------------------------------------------
// Get all records count for music and playlists
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetPlaylistUriArrayL(TInt aDrive, TInt aFromID, TInt aRecords,
                                         CDesCArray& aUriArr, TInt& aLastID)
    {
    MPX_FUNC("CMPXDbHandler::GetPlaylistUriArrayL");

    iDbPlaylist->GetPlaylistUriArrayL(aDrive,aFromID,aRecords,aUriArr,aLastID);
    }

// ----------------------------------------------------------------------------
// Starts a transaction on all databases
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::BeginTransactionL()
    {
    MPX_FUNC("CMPXDbHandler::BeginTransactionL");

#ifdef __RAMDISK_PERF_ENABLE
    // EnsureRamSpaceL will copy dbs from ram if ram space is low or dbs exceeded
    // max space.
	TRAPD(err, iDbManager->EnsureRamSpaceL());
	if (err)
		{
		//error but continue
		}
#endif //__RAMDISK_PERF_ENABLE

    if(!iMtpInUse)
        {
        iDbManager->BeginL();
        }
    }

// ----------------------------------------------------------------------------
// Ends a transaction on all databases
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::EndTransactionL(
    TInt aError)
    {
    MPX_FUNC("CMPXDbHandler::EndTransactionL");

    if(iMtpInUse)
        {
        if (aError)
            {
            iOpOnDbCount = 0;
            iDbManager->RollbackL();
            User::Leave(aError);
            }

        if(iOpOnDbCount >= KMaxOpInTransaction)
            {
            MPX_DEBUG2("CMPXDbHandler::EndTransactionL - %d>KMaxOpInTransaction Commiting",iOpOnDbCount);
            iOpOnDbCount = 0;
            iDbManager->CommitL();
            iDbManager->BeginL();
            }
        }
    else
        {
        if (aError)
            {
            MPX_DEBUG2("CMPXDbHandler::EndTransactionL - Rollback [%d]", aError);

            iDbManager->RollbackL();

            // KSqlDbCorrupted indicates DB corrupted, need to recreate.
            if ( aError != KSqlDbCorrupted )
                {
                User::Leave(aError);
                }
            }
        else
            {
            iDbManager->CommitL();
            }
        }
    }

// ----------------------------------------------------------------------------
// Checks if the database is currently in a transaction
// ----------------------------------------------------------------------------
//
TBool CMPXDbHandler::InTransaction()
    {
    MPX_FUNC("CMPXDbHandler::InTransaction");
    return iDbManager->InTransaction();
    }

// ----------------------------------------------------------------------------
// Notifies the handler that the collection will be closed.
// It is called before closing the collection.
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::PreCloseCollectionL()
    {
    MPX_FUNC("CMPXDbHandler::PreCloseCollectionL");
    // Complete pending transaction and set the latest refresh time
    // before closing the databases if collection close event occurs
    // before the end of the refresh operation
    if(iRefresh)
        {
        EndTransactionL(KErrNone);
        if (!iOutOfDisk)
            {
            // Write last refreshed time as current time
            // This also sets corrupt = 0
            TTime curTime;
            curTime.HomeTime();
            SetLastRefreshedTimeL(curTime);
            }
        }
    }

// ----------------------------------------------------------------------------
// Add song to collection
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::DoAddSongL(
    const CMPXMedia& aMedia,
    CMPXMessageArray* aMessageArray)
    {
    MPX_FUNC("CMPXDbHandler::DoAddSongL");

    if (!aMedia.IsSupported(KMPXMediaGeneralUri))
        {
        User::Leave(KErrArgument);
        }

    // add the song to the Music table
    TDriveUnit drive(aMedia.ValueText(KMPXMediaGeneralUri));
    TUint32 songId(iDbMusic->AddSongL(aMedia, drive,
                                      iRefresh?NULL:aMessageArray));

    // update the playlist tables
    // make sure the song db flags are reset and not just updated
    iDbPlaylist->UpdateSongL(aMedia, ETrue);

    if(iMtpInUse)
        {
        ++iOpOnDbCount;
        }

    return songId;
    }

// ----------------------------------------------------------------------------
// Add playlist to collection
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::DoAddPlaylistL(
    const CMPXMedia& aMedia)
    {
    MPX_FUNC("CMPXDbHandler::DoAddPlaylistL");

    CMPXMedia* media = CMPXMedia::NewL(aMedia);
    CleanupStack::PushL(media);

    const TDesC& playlistUri = media->ValueText(KMPXMediaGeneralUri);

    //
    // client has asked to create a virtual playlist if file name is not specified
    // in the URI. In this case, generate URI for virtual playlist specified as
    // follows:
    //          <dir path>\timestamp.vir
    //
    TParsePtrC parser(playlistUri);

    if (!parser.NameOrExtPresent() ||
        parser.IsNameWild() ||
        parser.IsExtWild())
        {
        HBufC* newUri = HBufC::NewLC(parser.DriveAndPath().Length() + KMCMaxTextLen +
            KMPXVirtualPlaylistExt().Length());

        TPtr ptr = newUri->Des();
        ptr.Append(parser.DriveAndPath());
        TTime time;
        time.HomeTime();
        ptr.AppendNum(time.Int64());
        ptr.Append(KMPXVirtualPlaylistExt);

        // overwrite the old uri with the new one with full path and playlist
        // playlist filename
        media->SetTextValueL(KMPXMediaGeneralUri, *newUri);

        CleanupStack::PopAndDestroy(newUri);

        // set DbFlags to indicate that this is a virtual playlist
        media->SetTObjectValueL<TUint>(KMPXMediaGeneralFlags,
            KMPXMediaGeneralFlagsSetOrUnsetBit | KMPXMediaGeneralFlagsIsVirtual);
        }

    // complete the song attributes from the music table
    UpdatePlaylistSongInfoL(*media);

    // add playlist to the database
    TUint32 playlistId = iDbPlaylist->AddPlaylistL(*media);

    CleanupStack::PopAndDestroy(media);
    return playlistId;
    }

// ----------------------------------------------------------------------------
// Add song to playlist
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::DoAddSongToPlaylistL(
    const CMPXMedia& aMedia)
    {
    MPX_FUNC("CMPXDbHandler::DoAddSongToPlaylistL");

    CMPXMedia* media = CMPXMedia::NewL(aMedia);
    CleanupStack::PushL(media);

    // complete the song attributes from the music table
    UpdatePlaylistSongInfoL(*media);

    // add the songs to the playlist
    TUint32 playlistId((media->ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId)).iId2);

    CMPXMediaArray* ary( aMedia.Value<CMPXMediaArray>(KMPXMediaArrayContents) );
    User::LeaveIfNull( ary );
    iDbPlaylist->AddSongsL(playlistId,*ary);

    CleanupStack::PopAndDestroy(media);
    return playlistId;
    }

// ----------------------------------------------------------------------------
// Update a song in the collection
// ----------------------------------------------------------------------------
//
CMPXDbActiveTask::TChangeVisibility CMPXDbHandler::DoUpdateSongL(
    const CMPXMedia& aMedia,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::DoUpdateSongL");

    CMPXDbActiveTask::TChangeVisibility visibleChange(CMPXDbActiveTask::ENotVisibile);

    TUint32 songId(0);

    if (aMedia.IsSupported(KMPXMediaGeneralId))
        {
        songId = (aMedia.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId)).iId2;
        }
    if (aMedia.IsSupported(KMPXMediaGeneralUri))
        {
        const TDesC& uri = aMedia.ValueText(KMPXMediaGeneralUri);
        songId = MPXDbCommonUtil::GenerateUniqueIdL(iFs, EMPXCollection, uri, EFalse);
        }
    if (!songId)
        {
        User::Leave(KErrNotSupported);
        }

    // Update the Music table
    TRAPD(err, visibleChange = iDbMusic->UpdateSongL(songId, aMedia, aItemChangedMessages));

    // do not leave if song is not found in Music table
    // leave for other errors such as disk full
    if(err != KErrNone && err != KErrNotFound)
        {
        User::Leave(err);
        }

    // Update the Playlist table
    TBool visible = EFalse;

    TRAP( err, visible = iDbPlaylist->UpdateSongL(aMedia, EFalse, &aItemChangedMessages));

    // do not leave if song is not found in Playlist table
    // leave for other errors such as disk full
    if(err != KErrNone && err != KErrNotFound)
        {
        User::Leave(err);
        }

    // make it visible if either table is updated
    if (visible)
        {
        visibleChange = CMPXDbActiveTask::EAllVisible;
        }
    if ( aMedia.ValueTObjectL<TInt>( KMPXMediaMusicAlbumArtChanged )== 1 )
        {
        ( const_cast<CMPXMedia*>( &aMedia ) 
        		)->SetTObjectValueL<TInt>( KMPXMediaMusicAlbumArtChanged, 0 );
        }

    return visibleChange;
    }

// ----------------------------------------------------------------------------
// Update a playlist in the collection
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoUpdatePlaylistL(
    const CMPXMedia& aMedia,
    CMPXMessageArray& aMessageArray)
    {
    MPX_FUNC("CMPXDbHandler::DoUpdatePlaylistL");

    TUint32 playlistId(0);
    TInt drive(0);

    CMPXMedia* media = CMPXMedia::NewL(aMedia);
    CleanupStack::PushL(media);


    ProcessPlaylistMediaL(*media, playlistId, drive);

    CMPXMessage* m1 = CMPXMessage::NewL();
    CleanupStack::PushL(m1);
    CMPXMessage* m2 = CMPXMessage::NewL();
    CleanupStack::PushL(m2);

    // send 2 messages to notify the playlist change & to refresh the display (update playlist name)
    MPXDbCommonUtil::FillItemChangedMessageL(*m1, playlistId, EMPXItemModified,
            EMPXPlaylist, KDBPluginUid);

    MPXDbCommonUtil::FillItemChangedMessageL(*m2, EBrowsePlaylist, EMPXItemModified,
                EMPXPlaylist, KDBPluginUid);

    iDbPlaylist->UpdatePlaylistL(*media, *m1, drive);

    aMessageArray.AppendL(*m1);
    aMessageArray.AppendL(*m2);


    CleanupStack::PopAndDestroy(m2);
    CleanupStack::PopAndDestroy(m1);
    CleanupStack::PopAndDestroy(media);
    }

// ----------------------------------------------------------------------------
// Update a playlist in the collection
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoUpdatePlaylistSongsL(
    const CMPXMedia& aMedia,
    CMPXMessage& aMessage)
    {
    MPX_FUNC("CMPXDbHandler::DoUpdatePlaylistSongsL");

    CMPXMedia* media = CMPXMedia::NewL(aMedia);
    CleanupStack::PushL(media);

    TUint32 playlistId(0);
    TInt drive(0);

    // get the playlist ID and drive ID
    ProcessPlaylistMediaL(*media, playlistId, drive);
    MPXDbCommonUtil::FillItemChangedMessageL(aMessage, playlistId, EMPXItemModified,
        EMPXPlaylist, KDBPluginUid);

    // complete the song attributes from the Music table
    UpdatePlaylistSongInfoL(*media);

    // delete existing songs for the playlist first
    iDbPlaylist->Songs().DeleteSongsL(playlistId, drive);

    // add new songs to the playlist
    CMPXMediaArray* ary( media->Value<CMPXMediaArray>(KMPXMediaArrayContents ) );
    User::LeaveIfNull( ary );
    iDbPlaylist->AddSongsL(playlistId, *ary);

    CleanupStack::PopAndDestroy(media);
    }

// ----------------------------------------------------------------------------
// Reorder a song in a playlist
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoReorderPlaylistL(
    const TMPXItemId& aPlaylistId,
    const TMPXItemId& aSongId,
    TUint aOriginalOrdinal,
    TUint aNewOrdinal,
    CMPXMessage& aMessage)
    {
    MPX_DEBUG1("-->CMPXDbHandler::DoReorderPlaylistL()");

    if (aOriginalOrdinal != aNewOrdinal)
        {
        iDbPlaylist->Songs().ReorderSongL(aPlaylistId, aSongId, aOriginalOrdinal, aNewOrdinal);

        MPXDbCommonUtil::FillItemChangedMessageL(aMessage, aPlaylistId.iId2, EMPXItemModified,
            EMPXPlaylist, KDBPluginUid);
        }

    MPX_DEBUG1("<--CMPXDbHandler::DoReorderPlaylistL()");
    }

// ----------------------------------------------------------------------------
// Delete a song from collection
// The function notifies collection model to perform deletion
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoRemoveSongL(
    TUint32 aSongId,
    CDesCArray& aUriArray,
    CMPXMessageArray& aItemChangedMessages,
    TBool aDeleteRecord)
    {
    MPX_FUNC("CMPXDbHandler::DoRemoveSongL");

    // Get the song drive
    TUint32 artistID(0);
    TUint32 albumID(0);
    TUint32 genreID(0);
    TUint32 composerID(0);
    HBufC*  art(NULL);
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    TUint32 abstractAlbumID(0);
#endif // ABSTRACTAUDIOALBUM_INCLUDED
    TInt drive(0);

// Get information from the Music table first
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    HBufC* uri = iDbMusic->GetSongInfoL(aSongId, artistID, albumID, genreID, composerID, abstractAlbumID, drive, art);
#else
    HBufC* uri = iDbMusic->GetSongInfoL(aSongId, artistID, albumID, genreID, composerID, drive, art);
#endif // ABSTRACTAUDIOALBUM_INCLUDED

    CleanupStack::PushL(art);
    // add the URI to the return array
    CleanupStack::PushL(uri);
    aUriArray.AppendL(*uri);
    CleanupStack::PopAndDestroy(uri);

    // Update the category records
    TBool categoryExist( EFalse );
    iDbArtist->DecrementSongsForCategoryL(artistID, drive, &aItemChangedMessages, categoryExist);
    iDbAlbum->DecrementSongsForCategoryL(albumID, drive, &aItemChangedMessages, categoryExist, artistID, *art);
    iDbGenre->DecrementSongsForCategoryL(genreID, drive, &aItemChangedMessages, categoryExist);
    iDbComposer->DecrementSongsForCategoryL(composerID, drive, &aItemChangedMessages, categoryExist);
    CleanupStack::PopAndDestroy(art);
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    if (abstractAlbumID)
        {
        iDbAbstractAlbum->DecrementSongsForCategoryL(abstractAlbumID, drive, &aItemChangedMessages, categoryExist, iMtpInUse);
        }
#endif // ABSTRACTAUDIOALBUM_INCLUDED
    // Update the music table
    TBool deleteRecord(ETrue);

#if defined (__MTP_PROTOCOL_SUPPORT)
    // Mark the song record as deleted if the following is true; otherwise, delete the
    // song record.
    //
    // A client other than MTP has initiated this song deletion (aDeleteRecord is EFalse)
    // and MTP has turned on its cenrep key to save deleted records and current number of
    // saved deleted records has not exceeded its maximum, KMCMaxSavedDeletedRecords.
    //
    // Songs are marked as deleted in order to support auto-sync. MTP will delete these
    // marked records at the end of each session via CleanupDeletedRecordsL.
    //
    // For performance consideration, if the number of saved records exceeds its maximum,
    // song record will be deleted.
    if (!aDeleteRecord && SaveDeletedSongs())
        {
        TUint32 savedDeletedRecordCount(iDbAuxiliary->SaveDeletedRecordCountL());
        MPX_DEBUG2("Current number of saved deleted record count is %d", savedDeletedRecordCount);

        if (savedDeletedRecordCount < KMCMaxSavedDeletedRecords)
            {
            deleteRecord = EFalse;
            TUint32 savedDeletedDriveRecordCount(iDbAuxiliary->SaveDeletedRecordCountL(drive));
            iDbAuxiliary->SetSaveDeletedRecordCountL(drive,++savedDeletedDriveRecordCount);
            }
        }
#endif

    // delete the song from the Music table
    iDbMusic->DeleteSongL(aSongId, drive, deleteRecord);

    // add the item changed message
    MPXDbCommonUtil::AddItemChangedMessageL(aItemChangedMessages, aSongId, EMPXItemDeleted,
        EMPXSong, KDBPluginUid);


    if(iMtpInUse)
        {
        ++iOpOnDbCount;
        }
    }

// ----------------------------------------------------------------------------
// Delete a song from playlist tables
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoRemoveSongFromPlaylistL(TUint32 aSongId,CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::DoRemoveSongFromPlaylistL");
    // delete song from the playlist tables on all drives
    iDbPlaylist->DeleteSongL(aSongId, aItemChangedMessages);
    }

// ----------------------------------------------------------------------------
// Removes a category of songs from the music collection,
// and its corresponding category in the lookup table
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoRemoveSongsMatchingCategoryL(
    TMPXGeneralCategory aCategory,
    TUint32 aCategoryId,
    CDesCArray& aUriArray,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::DoRemoveSongsMatchingCategoryL");

    // get the songs for the specified category
    CMPXMediaArray* songs = CMPXMediaArray::NewL();
    CleanupStack::PushL(songs);

    RArray<TMPXAttribute> attributes;
    CleanupClosePushL(attributes);
    attributes.AppendL(KMPXMediaGeneralId);

    switch (aCategory)
        {
        case EMPXArtist:
            {
            iDbMusic->GetSongsForArtistL(aCategoryId, attributes.Array(), *songs);
            break;
            }
        case EMPXAlbum:
            {
            iDbMusic->GetSongsForAlbumL(aCategoryId, attributes.Array(), *songs);
            break;
            }
        case EMPXGenre:
            {
            iDbMusic->GetSongsForGenreL(aCategoryId, attributes.Array(), *songs);
            break;
            }
        case EMPXComposer:
            {
            iDbMusic->GetSongsForComposerL(aCategoryId, attributes.Array(), *songs);
            break;
            }
        default:
            User::Leave(KErrNotSupported);
        }

    CleanupStack::PopAndDestroy(&attributes);

    // iterate the songs and remove them one by one
    // so records in the category tables can also be updated
    TInt count(songs->Count());
    for (TInt index = 0; index < count; ++index)
        {
        CMPXMedia* song = (*songs)[index];
        if (song->IsSupported(KMPXMediaGeneralId))
            {
            DoRemoveSongL((song->ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId)).iId2,
                aUriArray, aItemChangedMessages, EFalse);
            DoRemoveSongFromPlaylistL((song->ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId)).iId2,aItemChangedMessages);
            }
        }

    CleanupStack::PopAndDestroy(songs);
    }

// ----------------------------------------------------------------------------------------------------------
// Delete songs for the specified artist and album from collection
// ----------------------------------------------------------------------------------------------------------
//
void CMPXDbHandler::DoRemoveSongsMatchingArtistAndAlbumL(
    TUint32 aArtistId,
    TUint32 aAlbumId,
    CDesCArray& aUriArray,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::RemoveSongsMatchingArtistAndAlbumL");

    // get the songs for the specified artist and album
    CMPXMediaArray* songs = CMPXMediaArray::NewL();
    CleanupStack::PushL(songs);

    RArray<TMPXAttribute> attributes;
    CleanupClosePushL (attributes);
    attributes.AppendL(KMPXMediaGeneralId);

    iDbMusic->GetSongsForArtistAndAlbumL(aArtistId, aAlbumId, attributes.Array(), *songs);
    CleanupStack::PopAndDestroy(&attributes);

    // iterate the songs and remove them one by one
    // so records in the category tables can also be updated
    TInt count(songs->Count());
    for (TInt index = 0; index < count; ++index)
        {
        CMPXMedia* song = (*songs)[index];
        if (song->IsSupported(KMPXMediaGeneralId))
            {
            DoRemoveSongL( song->ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId),
                aUriArray, aItemChangedMessages, EFalse);
            DoRemoveSongFromPlaylistL(song->ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId),aItemChangedMessages);
            }
        }

    CleanupStack::PopAndDestroy(songs);
    }

// ----------------------------------------------------------------------------
// Remove all playlists from collection
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoRemoveAllPlaylistsL()
    {
    MPX_FUNC("CMPXDbHandler::DoRemoveAllPlaylistsL");
    iDbPlaylist->DeleteAllPlaylistsL();
    }

// ----------------------------------------------------------------------------
// Remove specified playlist
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoRemovePlaylistL(
    TUint32 aPlaylistId,
    CDesCArray& aUriArray,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::DoRemovePlaylistL");

    HBufC* uri(iDbPlaylist->DeletePlaylistL(aPlaylistId));
    if (uri)
        {
        CleanupStack::PushL(uri);
        aUriArray.AppendL(*uri);
        CleanupStack::PopAndDestroy(uri);
        }

    MPXDbCommonUtil::AddItemChangedMessageL(aItemChangedMessages, aPlaylistId, EMPXItemDeleted,
        EMPXPlaylist, KDBPluginUid);
    }

// ----------------------------------------------------------------------------
// Remove song from playlist songs table
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoRemoveSongFromPlaylistL(
    TUint32 aPlaylistId,
    const TMPXItemId& aSongId,
    TInt aOrdinal,
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::DoRemoveSongFromPlaylistL");
    MPX_DEBUG5("CMPXDbHandler::DoRemoveSongFromPlaylistL(playlist 0x%x, songId [0x%x,0x%x], ordinal %d)",
        aPlaylistId, aSongId.iId1, aSongId.iId2, aOrdinal);

    // delete the song
    iDbPlaylist->DeleteSongL(aPlaylistId, aSongId.iId2, aOrdinal);

    // Send a playlist modified message
    MPXDbCommonUtil::AddItemChangedMessageL(aItemChangedMessages, aPlaylistId, EMPXItemModified,
        EMPXPlaylist, KDBPluginUid);

    // Send a message on the song in the playlist that is deleted
    MPXDbCommonUtil::AddItemChangedMessageL(aItemChangedMessages, aSongId, EMPXItemDeleted,
        EMPXSong, KDBPluginUid);
    }
    

// ----------------------------------------------------------------------------
// CMPXDbHandler::DoCleanupDeletedRecordsL
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DoCleanupDeletedRecordsL()
    {
    MPX_FUNC("CMPXDbHandler::DoCleanupDeletedRecordsL");

    // delete all marked records from the Music table
    iDbMusic->CleanupL();

    // reset the count in the Auxiliary table
    iDbAuxiliary->SetSaveDeletedRecordCountL(KDbManagerAllDrives,0);
    }

// ----------------------------------------------------------------------------
// FindAllL
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::FindAllL(
    const CMPXMedia& aCriteria,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray* aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::FindAllL");

    RArray<TMPXAttribute> attributes;
    CleanupClosePushL(attributes);
    MPXUser::MergeAttributeL(aAttrs, attributes);

    TMPXGeneralCategory category = aCriteria.ValueTObjectL<TMPXGeneralCategory>(KMPXMediaGeneralCategory);
    switch (category)
        {
        case EMPXPlaylist:
            {
            TUint32 playlistId(0);
            if (aCriteria.IsSupported(KMPXMediaGeneralId))
                {
                playlistId = (aCriteria.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId)).iId2;
                }

            if (iAutoPlaylist->AutoPlaylistTypeL(playlistId) != EMPXNoAutoPlaylist)
                {
                CMPXMedia* media = CMPXMedia::NewL();
                CleanupStack::PushL(media);

                iAutoPlaylist->GetPlaylistL(playlistId, aAttrs, *media);

                aMediaArray->AppendL(*media);
                CleanupStack::PopAndDestroy(media);
                }
            else
                {
                iDbPlaylist->FindAllL(aCriteria, attributes.Array(), *aMediaArray);
                }

            break;
            }
        case EMPXSong:
            {
            FindSongL(aCriteria, attributes.Array(), *aMediaArray);
            break;
            }
        default:
            {
            DbCategoryL(category)->FindAllL(aCriteria, attributes.Array(), *aMediaArray);
            break;
            }
        }

    CleanupStack::PopAndDestroy(&attributes);
    }

// ----------------------------------------------------------------------------
// Get song(s) from the music table that match the given criteria
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::FindSongL(
    const CMPXMedia& aCriteria,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray& aMediaArray)
    {
    MPX_FUNC("CMPXDbCollection::FindSongL");

    TMPXGeneralType type = aCriteria.ValueTObjectL<TMPXGeneralType>(KMPXMediaGeneralType);

    TUint32 id(0);
    if (aCriteria.IsSupported(KMPXMediaGeneralId))
        {
        id = (aCriteria.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId)).iId2;
        }

    TUint32 containerId(0);
    if (aCriteria.IsSupported(KMPXMediaGeneralContainerId))
        {
        containerId = aCriteria.ValueTObjectL<TUint32>(KMPXMediaGeneralContainerId);
        }

    //////////////////////////////////////////////////////////////////////
    // Find songs in the specified playlist
    //////////////////////////////////////////////////////////////////////
    TMPXGeneralCategory cat(MPX_ITEM_CATEGORY(id));

    if (type == EMPXGroup &&
        (cat == EMPXPlaylist ||
        MPX_ITEM_CATEGORY(containerId) == EMPXPlaylist))
        {
        TUint32 playlistId = (cat == EMPXPlaylist) ?
            id : (containerId & KMCCategoryMask);

        GetPlaylistSongsL(playlistId, aAttrs, aMediaArray);
        }
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    else if (type == EMPXGroup &&
        (cat == EMPXAbstractAlbum ||
        MPX_ITEM_CATEGORY(containerId) == EMPXAbstractAlbum))
        {
        TUint32 abstractAlbumId = (cat == EMPXAbstractAlbum) ?
            id : (containerId & KMCCategoryMask);
        TInt attrCount(aAttrs.Count());
        if ( attrCount > 1 || (attrCount == 1 && !(aAttrs[0] == KMPXMediaGeneralId)) )
            {
            MPX_TRAPD(err, iDbMusic->GetAllSongsForAbstractAlbumL( abstractAlbumId, aAttrs, aMediaArray));
        if (err == KErrNotFound)
            {
            //
            // Leave with KErrNotFound
            User::Leave(KErrNotFound);
            }
        else
            {
            // Leave if error
            User::LeaveIfError(err);
            }
        }
    }
#endif // ABSTRACTAUDIOALBUM_INCLUDED
    //////////////////////////////////////////////////////////////////////
    // Find a particular song in the specified playlist. This fills the
    // song with info from Playlist table first then overwrites it with
    // info from Songs table if Songs table where this song is located
    // is present in order to support the display of song titles in a
    // playlist when memory card is removed if the playlist refers to
    // songs on the memory card. Caller of this scenario is OpenL/MediaL.
    // When user attempts to play a track in an auto-playlist, we will
    // find the song from Songs table directly since auto-playlists are
    // not stored in the Playlist table. Auto-playlists are query-based,
    // therefore, when memory card is removed, songs on the memory card
    // will not be shown in the auto-playlist; hence they do not exhibit
    // the same challenge as user created playlists.
    //////////////////////////////////////////////////////////////////////
    else if (type == EMPXItem &&
        cat == EMPXCollection &&
        MPX_ITEM_CATEGORY(containerId) == EMPXPlaylist)
        {
        if (iAutoPlaylist->AutoPlaylistTypeL(containerId) != EMPXNoAutoPlaylist)
            {
            // auto playlist song, get the song details from the music table
            iDbMusic->FindSongsL(id, 0, type, aCriteria, aAttrs, aMediaArray);
            }
        else
            {
            GetPlaylistSongL(id, containerId, aAttrs, aMediaArray);
            }
        }

    //////////////////////////////////////////////////////////////////////
    // Find all songs, all songs in a particular album and/or artist, or
    // a particular song
    //////////////////////////////////////////////////////////////////////
    else
        {
        iDbMusic->FindSongsL(id, containerId, type, aCriteria, aAttrs, aMediaArray);
        }
    }

// ----------------------------------------------------------------------------
// Get song(s) in the specified playlist
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::GetPlaylistSongsL(
    TUint32 aPlaylistId,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray& aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::GetPlaylistSongsL");
    MPX_DEBUG2("CMPXDbHandler::GetPlaylistSongsL(0x%x)", aPlaylistId);

    // check the auto playlists first
    if (aPlaylistId == iAutoPlaylist->AutoPlaylistIdL(EMPXRecentlyPlayedPlaylist))
        {
        iDbMusic->GetRecentlyPlayedSongsL(aAttrs, aMediaArray);
        }
    else if (aPlaylistId == iAutoPlaylist->AutoPlaylistIdL(EMPXMostPlayedPlaylist))
        {
        iDbMusic->GetMostPlayedSongsL(aAttrs, aMediaArray);
        }
    else if (aPlaylistId == iAutoPlaylist->AutoPlaylistIdL(EMPXRecentlyAddedPlaylist))
        {
        iDbMusic->GetRecentlyAddedSongsL(aAttrs, aMediaArray);
        }
    else
        {
        TInt attrCount(aAttrs.Count());
        if ( attrCount > 1 || (attrCount == 1 && !(aAttrs[0] == KMPXMediaGeneralId)) )
            {
	        TInt plDrive(iDbPlaylist->GetDriveIdL(aPlaylistId));
	        MPX_TRAPD(err, iDbMusic->GetAllSongsL(plDrive, aPlaylistId, aAttrs, aMediaArray));

	        // song not found in Music table
	        if (err == KErrNotFound)
	            {
	            //
	            // Leave with KErrNotFound if one of the following is true:
	            // 1) the requested song is in an auto playlist. Since auto-playlist isn't
	            //    stored in playlist tables, we won't be able to retrieve info elsewhere
	            // 2) the requested song is in a user playlist but we cannot find the song
	            //    info from playlist tables either
	            //
	           if (EMPXNoAutoPlaylist != iAutoPlaylist->AutoPlaylistTypeL(aPlaylistId) ||
	                !iDbPlaylist->Songs().GetSongsL(aPlaylistId, aAttrs, aMediaArray))
	               {
	               User::Leave(KErrNotFound);
	               }
	            }
	        else
	            {
	            // ignore the error if KErrNotFound
	            User::LeaveIfError(err);
	            }
            }
        else
            {
            // get ids of the songs in the playlist
            iDbPlaylist->Songs().GetSongsL(aPlaylistId, aMediaArray);
            }
        }
    }

// ----------------------------------------------------------------------------
// Find all albums or the albums for a specified artist
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::FindAlbumL(
    const CMPXMedia& aCriteria,
    const TArray<TMPXAttribute>& aAttrs,
    CMPXMediaArray& aMediaArray)
    {
    MPX_FUNC("CMPXDbHandler::FindAlbumL");

    TMPXGeneralType type = aCriteria.ValueTObjectL<TMPXGeneralType>(KMPXMediaGeneralType);

    TUint32 id(0);
    if (aCriteria.IsSupported(KMPXMediaGeneralId))
        {
        id = aCriteria.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId);
        }

    if ((type == EMPXGroup) && (MPX_ITEM_CATEGORY(id) == EMPXArtist))
        {
        // get all the albums for the artist
        GetAlbumsMatchingArtistL(id, aAttrs, aMediaArray);
        }
    else
        {
        // look up all albums from album table
        iDbAlbum->FindAllL(aCriteria, aAttrs, aMediaArray);
        }
    }

// ----------------------------------------------------------------------------
// Extracts the playlist ID and drive ID from a playlist media instance
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::ProcessPlaylistMediaL(
    CMPXMedia& aMedia,
    TUint32& aPlaylistId,
    TInt& aPlaylistDriveId)
    {
    MPX_FUNC("CMPXDbHandler::ProcessPlaylistMediaL");

    if (aMedia.IsSupported(KMPXMediaGeneralId))
        {
        aPlaylistId = aMedia.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId);

        if (aMedia.IsSupported(KMPXMediaGeneralUri))
            {
            // find drive id of the playlist
            aPlaylistDriveId = TDriveUnit(aMedia.ValueText(KMPXMediaGeneralUri));
            }
        else
            {
            // Find drive Id(s) of corresponding Playlist Id
            aPlaylistDriveId = iDbPlaylist->GetDriveIdL(aPlaylistId);
            }
        }
    else if (aMedia.IsSupported(KMPXMediaGeneralUri))
        {
        const TDesC& playlistUri = aMedia.ValueText(KMPXMediaGeneralUri);
        // find drive id of the playlist
        aPlaylistDriveId = TDriveUnit(playlistUri);

        // aMedia does not have an ID, make sure the add it
        aPlaylistId = GetPlaylistIdMatchingUriL(playlistUri);
        aMedia.SetTObjectValueL<TMPXItemId>(KMPXMediaGeneralId, aPlaylistId);
        }
    else
        {
        User::Leave(KErrArgument);
        }
    }

// ----------------------------------------------------------------------------
// Makes sure that all songs in the specified playlist have the ID, title and URI attributes
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::UpdatePlaylistSongInfoL(
    CMPXMedia& aMedia)
    {
    MPX_FUNC("CMPXDbHandler::UpdatePlaylistSongInfoL");

    CMPXMediaArray* mediaArray = aMedia.Value<CMPXMediaArray>(KMPXMediaArrayContents);
    User::LeaveIfNull(mediaArray);

    // make sure each song has Id, Uri, and Title before they can be added to playlist
    TInt count(mediaArray->Count());
    for (TInt i = 0; i < count; ++i)
        {
        CMPXMedia* element = mediaArray->AtL(i);

        // copy each song to deal w/ global heap issues
        CMPXMedia* entry = CMPXMedia::NewL(*element);
        CleanupStack::PushL(entry);

        // song has everything, go to next song
        if (entry->IsSupported(KMPXMediaGeneralUri) &&
            entry->IsSupported(KMPXMediaGeneralId) &&
            entry->IsSupported(KMPXMediaGeneralTitle))
            {
            // pop entry to maintain CleanupStack
            CleanupStack::PopAndDestroy(entry);
            continue;
            }

        // songs must contain (at minimum) an Uri or an Id
        if (!entry->IsSupported(KMPXMediaGeneralUri) &&
            !entry->IsSupported(KMPXMediaGeneralId))
            {
            User::Leave(KErrArgument);
            }

        // get Id
        if (!entry->IsSupported(KMPXMediaGeneralId))
            {
            // fill in the ID if not present
            TParsePtrC parser(entry->ValueText(KMPXMediaGeneralUri));
            entry->SetTObjectValueL<TMPXItemId>(KMPXMediaGeneralId,
                MPXDbCommonUtil::GenerateUniqueIdL(iFs, EMPXCollection, parser.FullName(), EFalse));
            }

        CMPXMedia* song(NULL);

        // update songs info
        TInt error(iDbMusic->GetSongL(*entry, song));
        TBool result (ETrue);

        // error can only be KErrNone or KErrNotFound
        // from CMPXDbMusic::GetSongL
        // if not found in Music, get info from PlaylistSongs (PlaylistSongs & PlaylistSongInfo) DB
        if (error == KErrNotFound)
            {
            RArray<TMPXAttribute> attributes;
            CleanupClosePushL(attributes);
            attributes.AppendL(TMPXAttribute(KMPXMediaIdGeneral,
                EMPXMediaGeneralId | EMPXMediaGeneralTitle | EMPXMediaGeneralUri | EMPXMediaGeneralFlags));

            // this song doesn't exist in Music table. This song is either a broken link or
            // is of an unsupported song type that exists in the file system. Broken links
            // have already been marked as such during playlist import.
            result = iDbPlaylist->Songs().GetSongL(entry->ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId), attributes.Array(), song);
            if (!result)
                {
                // song is a broken link
                //TUint flags = KMPXMediaGeneralFlagsSetOrUnsetBit;
                //flags |= KMPXMediaGeneralFlagsIsInvalid; // set flag
                //t->SetTObjectValueL<TUint>( KMPXMediaGeneralFlags, flags );

                if (entry->IsSupported(KMPXMediaGeneralUri))
                    {
                    // no valid Id but has Uri, just verify Title is present
                    // this is the case if the song is a broken link or podcast
                    if (!entry->IsSupported(KMPXMediaGeneralTitle))
                        {
                        // does not have Title, make up the Title from file name
                        TParsePtrC parser(entry->ValueText(KMPXMediaGeneralUri));
                        entry->SetTextValueL(KMPXMediaGeneralTitle, parser.Name());
                        }
                    }
                else
                    {
                    // no valid Id & no Uri, bad argument
                    User::Leave(KErrArgument);
                    }
                }
            CleanupStack::PopAndDestroy(&attributes);
            }

        // update attributes
        CleanupStack::PushL(song);

        // song not found in Music or Playlist DB, update entry
        if(error == KErrNotFound && !result)
            {
            mediaArray->InsertL(*entry,i);
            }
        else  // found in DB, replace entry
            {
            mediaArray->InsertL(*song,i);
            }

        // replace element in the array
        CleanupStack::PopAndDestroy(song);
        CleanupStack::PopAndDestroy(entry);
        mediaArray->Remove(i+1);
        }
    }

// ----------------------------------------------------------------------------
// CMPXDbHandler::ProcessMusicFoldersL
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::ProcessMusicFoldersL(
    const CDesCArray& aFolders)
    {
    MPX_FUNC("CMPXDbHandler::ProcessMusicFoldersL");

    TInt count(aFolders.MdcaCount());
    for (TInt i = 0; i < count; ++i)
        {
        TPtrC16 folder = aFolders.MdcaPoint(i);

        // check if disk is inserted and act accordingly
        TDriveUnit driveUnit(folder);
        if (!iFs.IsValidDrive(driveUnit))
            {
            User::Leave(KErrArgument);
            }

        // append the drive to the drive list
        iDbDrives.AppendL(driveUnit);
        }
    }

// ----------------------------------------------------------------------------
// CMPXDbHandler::DbCategoryL
// ----------------------------------------------------------------------------
//
CMPXDbCategory* CMPXDbHandler::DbCategoryL(
    TMPXGeneralCategory aCategory) const
    {
    MPX_FUNC("CMPXDbHandler::DbCategoryL");

    CMPXDbCategory* dbCategory(NULL);
    switch (aCategory)
        {
        case EMPXArtist:
            {
            dbCategory = (CMPXDbCategory*)iDbArtist;
            break;
            }
        case EMPXAlbum:
            {
            dbCategory = (CMPXDbCategory*)iDbAlbum;
            break;
            }
        case EMPXGenre:
            {
            dbCategory = (CMPXDbCategory*)iDbGenre;
            break;
            }
        case EMPXComposer:
            {
            dbCategory = (CMPXDbCategory*)iDbComposer;
            break;
            }
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
        case EMPXAbstractAlbum:
            {
            dbCategory = (CMPXDbCategory*)iDbAbstractAlbum;
            break;
            }
#endif // ABSTRACTAUDIOALBUM_INCLUDED
        default:
            User::Leave(KErrNotSupported);
        }

    return dbCategory;
    }

// ----------------------------------------------------------------------------
// Verifies that the volume ID of the database matches the drive
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::VerifyVolumeIdL( TInt aDrive )
    {
    MPX_DEBUG1("CMPXDbHandler::VerifyVolumeIdL <--");
        if( iDbManager->IsOpen( aDrive ) )
            {
            TVolumeInfo volInfo;
            iFs.Volume(volInfo, aDrive );
            TUint curId(volInfo.iUniqueID);

            TInt volId = iDbAuxiliary->IdL( aDrive );

            // New database, no volume id set, mask out top bit because this is an uint
            //
            MPX_DEBUG3("CMPXDBHandler::VerifyVolumeIdL drive:%i db:%i", curId, volId);
            if( volId == 0 && (curId&0x7FFFFFFF) )
                {
                MPX_DEBUG1("CMPXDbHandler::VerifyVolumeIdL -- New ID");
                BeginTransactionL();
                TRAPD( err, iDbAuxiliary->SetIdL( aDrive, curId&0x7FFFFFFF ) );
                EndTransactionL( err );

                // KSqlDbCorrupted indicates DB corrupted, need to recreate.
                if ( err == KSqlDbCorrupted )
                    {
                    MPX_DEBUG1("CMPXPodcastDbHandler::VerifyVolumeIdL -- Corrupted DB");
                    iDbManager->RecreateDatabaseL(aDrive);
                    BeginTransactionL();
                    TRAPD(err, iDbAuxiliary->SetDBCorruptedL( ETrue ) );
                    EndTransactionL( err );
                    }
                }
            // Unmatched volume id, mark db as corrupt and break
            //
            else if ( (curId&0x7FFFFFFF) != (volId&0x7FFFFFFFF) )
                {
                MPX_DEBUG1("CMPXDbHandler::VerifyVolumeIdL -- ID match FAILED");
                iDbManager->RecreateDatabaseL(aDrive);
                BeginTransactionL();
                TRAPD(err, iDbAuxiliary->SetDBCorruptedL( ETrue ) );
                EndTransactionL( err );
                }
            }

    MPX_DEBUG1("CMPXDbHandler::VerifyVolumeIdL -->");
    }

// ----------------------------------------------------------------------------
// Verifies that the volume ID of the database matches the drive
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::VerifyVolumeIdL()
    {
    MPX_DEBUG1("CMPXDbHandler::VerifyVolumeIdL <--");
    TInt count( iDbDrives.Count() );
    for( TInt i=0; i<count; ++i )
        {
        VerifyVolumeIdL(iDbDrives[i]);
        }
    MPX_DEBUG1("CMPXDbHandler::VerifyVolumeIdL -->");
    }


// ----------------------------------------------------------------------------
// Checks if there is a drive that has a low disk space
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::CheckDiskSpaceOnDrivesL()
    {
    MPX_DEBUG1("CMPXDbHandler::CheckDiskSpaceOnDrivesL <--");

    TInt count( iDbDrives.Count() );
    for( TInt index=0; index<count; ++index )
        {
        iDbManager->CheckDiskSpaceL(iDbDrives[index]);
        }
    MPX_DEBUG1("CMPXDbHandler::CheckDiskSpaceOnDrivesL -->");
    }

#if defined (__MTP_PROTOCOL_SUPPORT)

// ----------------------------------------------------------------------------
// CMPXDbHandler::SaveDeletedSongs
// ----------------------------------------------------------------------------
//
TBool CMPXDbHandler::SaveDeletedSongs()
    {
    MPX_FUNC("CMPXDbHandler::SaveDeletedSongs");

    TBool saveDeletedSongs(ETrue);
    CRepository* cenrep(NULL);
    MPX_TRAPD(error, cenrep = CRepository::NewL(KMPXMtpSettings));
    if (!error)
        {
        cenrep->Get(KMPXMtpSaveDeletedRecordFlag, saveDeletedSongs);
        delete cenrep;
        MPX_DEBUG2("MTP indicated to save deleted songs? %d", saveDeletedSongs);
        }

    return saveDeletedSongs;
    }

#endif

#ifdef RD_MULTIPLE_DRIVE

// ----------------------------------------------------------------------------------------------------------
// Retrieve all visible music folder locations
// ----------------------------------------------------------------------------------------------------------
//
CDesCArrayFlat* CMPXDbHandler::GetMusicFoldersL()
    {
    MPX_FUNC("CMPXDbHandler::GetMusicFoldersL()");
    TDriveList driveList;
    TInt driveCount(0);
    User::LeaveIfError(DriveInfo::GetUserVisibleDrives(iFs, driveList, driveCount));
    MPX_DEBUG2 ("CMPXDbHandler::GetMusicFoldersL() - driveCount = %d", driveCount);

    CDesCArrayFlat* folders = new (ELeave) CDesCArrayFlat(1); // granularity
    CleanupStack::PushL(folders);

    for (TInt i = EDriveA; i <= EDriveZ; i++)
        {
        if ((driveList[i]) && (!IsRemoteDrive(static_cast<TDriveNumber>(i))))
            {
            if (i == EDriveC)
                {
                // Append the default phone memory path to the list
                // of music folders
                TPtrC rootPath(PathInfo::PhoneMemoryRootPath());
                folders->AppendL(rootPath);
                MPX_DEBUG2("CMPXDbHandler::GetMusicFoldersL() - adding...%S", &rootPath);
                }
            else
                {
                // Get drive letter
                TChar driveChar;
                User::LeaveIfError(iFs.DriveToChar(i, driveChar));

                // Append visible drive to list of music folders
                TBuf<2> drive;
                drive.Append(driveChar);
                drive.Append(_L(":"));
                folders->AppendL(drive);
                MPX_DEBUG2 ("CMPXDbHandler::GetMusicFoldersL() - adding...%S", &drive);
                }
            }
        }

    CleanupStack::Pop(folders);
    return folders;
    }

#endif // RD_MULTIPLE_DRIVE

// ----------------------------------------------------------------------------
// CMPXDbHandler::AddCategoryItemL
// ----------------------------------------------------------------------------
//
TUint32 CMPXDbHandler::AddCategoryItemL(
    TMPXGeneralCategory aCategory,
    const TDesC& aName,
    TInt aDriveId,
    CMPXMessageArray* aItemChangedMessages,
    TBool& aItemExist
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    ,const TDesC& aUri
    ,const TDesC& aAlbumArtist
#endif // ABSTRACTAUDIOALBUM_INCLUDED
    )
    {
    MPX_FUNC("CMPXDbHandler::AddCategoryItemL()");

    MPX_PERF_START(CMPXDbHandler_AddCategoryItemL);

    TBool newRecord(EFalse);
#ifdef ABSTRACTAUDIOALBUM_INCLUDED
    TUint32 id(0);
    //for AbstractAlbum, SetAbstractAlbum, associate songs with abstractalbum.
    //only called with newRecord as EFalse
    //increment songCount if id exist in AbstractAlbum table, otherwise do nothing.

    //only Genre, AbstractAlbum are not case sensitive
    TBool caseSensitive = ETrue;
    if ((aCategory == EMPXGenre) || (aCategory == EMPXAbstractAlbum))
        caseSensitive = EFalse;

    if (aCategory == EMPXAbstractAlbum)
        {
        id =  iDbAbstractAlbum->AddItemL(aUri, aName, aAlbumArtist, aDriveId, newRecord, caseSensitive);
        }
    else
        {
        id = DbCategoryL(aCategory)->AddItemL(aName, aDriveId, newRecord, caseSensitive);
#else
    TUint32 id(DbCategoryL(aCategory)->AddItemL(aName, aDriveId, newRecord, (aCategory != EMPXGenre)));
#endif // ABSTRACTAUDIOALBUM_INCLUDED

    if (newRecord && aItemChangedMessages)
        {
        MPXDbCommonUtil::AddItemChangedMessageL(*aItemChangedMessages, id, EMPXItemInserted,
            aCategory, KDBPluginUid);
        }
    // when the added item's category is Genre or Composer,
    // and it is NOT a new record,
    // we should send the item number changed message
    else if ( ( aCategory == EMPXGenre || aCategory == EMPXComposer ) &&
            !newRecord && aItemChangedMessages )
        {
        MPXDbCommonUtil::AddItemChangedMessageL(*aItemChangedMessages, id, EMPXItemModified,
            aCategory, KDBPluginUid);
        }
    aItemExist = !newRecord;
#ifdef ABSTRACTAUDIOALBUM_INCLUDED      
       }
#endif // ABSTRACTAUDIOALBUM_INCLUDED
    MPX_PERF_END(CMPXDbHandler_AddCategoryItemL);
    return id;
    }

TUint32 CMPXDbHandler::AddCategoryItemL(
        TMPXGeneralCategory aCategory,
        const TDesC& aName,
        const TDesC& aArtistName,
        const TDesC& aArt,
        TInt aDriveId,
        CMPXMessageArray* aItemChangedMessages,
        TBool& aItemExist)
	{
    MPX_FUNC("CMPXDbHandler::AddCategoryItemL()");

    MPX_PERF_START(CMPXDbHandler_AddCategoryItemL);

    TBool newRecord(EFalse);

    TUint32 id = 0;
    if(aCategory == EMPXAlbum)
        {
        id = iDbAlbum->AddItemL(aName, aArtistName, aArt, aDriveId, newRecord, (aCategory != EMPXGenre));
        }
    else
        {
        id = iDbArtist->AddItemL(aName, aArt, aDriveId, newRecord, (aCategory != EMPXGenre));
        }

    if (newRecord && aItemChangedMessages)
        {
        MPXDbCommonUtil::AddItemChangedMessageL(*aItemChangedMessages, id, EMPXItemInserted,
            aCategory, KDBPluginUid);
        }
    // when the added item's category is Artist, and it is NOT a new record,
    // we should send the item number changed message
    else if (  aCategory == EMPXArtist &&
            !newRecord && aItemChangedMessages )
        {
        MPXDbCommonUtil::AddItemChangedMessageL(*aItemChangedMessages, id, EMPXItemModified,
            aCategory, KDBPluginUid);
        }
    aItemExist = !newRecord;
    MPX_PERF_END(CMPXDbHandler_AddCategoryItemL);

    return id;
    }

void CMPXDbHandler::UpdateCategoryItemL(
        TMPXGeneralCategory aCategory,
        TUint32 aCategoryId,
        const CMPXMedia& aMedia,
        TInt aDrive,
        CMPXMessageArray* aItemChangedMessages)
    {
    switch(aCategory)
        {
        case EMPXAlbum:
            iDbAlbum->UpdateItemL(aCategoryId, aMedia, aDrive, aItemChangedMessages);
            break;

        case EMPXArtist:
            iDbArtist->UpdateItemL(aCategoryId, aMedia, aDrive, aItemChangedMessages);
            break;

        default:
            DbCategoryL(aCategory)->UpdateItemL(aCategoryId, aMedia, aDrive, aItemChangedMessages);
            break;
        }
    }
// ----------------------------------------------------------------------------
// CMPXDbHandler::DeleteSongForCategoryL
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::DeleteSongForCategoryL(
    TMPXGeneralCategory aCategory,
    TUint32 aCategoryId,
    TInt aDriveId,
    CMPXMessageArray* aItemChangedMessages,
    TBool& aItemExist)
    {
    MPX_FUNC("CMPXDbHandler::DeleteSongForCategoryL");
    DbCategoryL(aCategory)->DecrementSongsForCategoryL(aCategoryId, aDriveId,
        aItemChangedMessages, aItemExist);
    }

// ----------------------------------------------------------------------------
// CMPXDbHandler::HandlePlayCountModifiedL
// ----------------------------------------------------------------------------
//
void CMPXDbHandler::HandlePlayCountModifiedL(
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::HandlePlayCountModifiedL");

    TUint32 plId(iAutoPlaylist->AutoPlaylistIdL(EMPXMostPlayedPlaylist));

    MPXDbCommonUtil::AddItemChangedMessageL(aItemChangedMessages, plId, EMPXItemModified,
        EMPXSong, KDBPluginUid, plId);

    // Force the deprecated ID attribute
    aItemChangedMessages[aItemChangedMessages.Count() - 1]->
        SetTObjectValueL<TMPXItemId>(KMPXMessageMediaDeprecatedId, plId);
    }

// ----------------------------------------------------------------------------------------------------------
// CMPXDbHandler::HandlePlaybackTimeModifiedL
// ----------------------------------------------------------------------------------------------------------
//
void CMPXDbHandler::HandlePlaybackTimeModifiedL(
    CMPXMessageArray& aItemChangedMessages)
    {
    MPX_FUNC("CMPXDbHandler::HandlePlaybackTimeModifiedL");

    TUint32 plId(iAutoPlaylist->AutoPlaylistIdL(EMPXRecentlyPlayedPlaylist));

    MPXDbCommonUtil::AddItemChangedMessageL(aItemChangedMessages, plId, EMPXItemModified,
        EMPXSong, KDBPluginUid, plId);

    // Force the deprecated ID attribute
    aItemChangedMessages[aItemChangedMessages.Count() - 1]->
        SetTObjectValueL<TMPXItemId>(KMPXMessageMediaDeprecatedId, plId);
    }


// ---------------------------------------------------------------------------
// CMPXDbHandler::IsRemoteDrive
// ---------------------------------------------------------------------------
//
TBool CMPXDbHandler::IsRemoteDrive(TDriveNumber aDrive)
    {
    return iDbManager->IsRemoteDrive(aDrive);
    }

TInt CMPXDbHandler::HandlePlaylistDurationL(TUint32 aPlaylistId)
    {
    return GetPlaylistDurationL(aPlaylistId);
    }
void CMPXDbHandler::HandlePlaylistInfoL(
    TUint32 aPlaylistId, 
    TInt& aCount, 
    TInt& aDuration)
    {
    MPX_FUNC("CMPXDbHandler::HandlePlaylistInfoL");
    MPX_DEBUG2("CMPXDbHandler::HandlePlaylistInfoL(0x%x)", aPlaylistId);

    RArray<TMPXAttribute> attributes;
    CleanupClosePushL(attributes);
    attributes.AppendL(KMPXMediaGeneralId);    
    attributes.AppendL(TMPXAttribute(KMPXMediaIdGeneral, EMPXMediaGeneralDuration));

    CMPXMediaArray* mediaArray = CMPXMediaArray::NewL();
    CleanupStack::PushL(mediaArray);    
    
    GetPlaylistSongsL(aPlaylistId, attributes.Array(), *mediaArray);
    
    aCount = mediaArray->Count();
    for (TInt index = 0; index < aCount; ++index)
        {
        CMPXMedia* media((*mediaArray)[index]);
        if (media->IsSupported(KMPXMediaGeneralDuration))
            {
            aDuration += media->ValueTObjectL<TInt>(KMPXMediaGeneralDuration);
            }
        }

    CleanupStack::PopAndDestroy(mediaArray);
    CleanupStack::PopAndDestroy(&attributes);        
    }

TInt CMPXDbHandler::HandleGetAlbumsCountForArtistL(TUint32 aArtistId)
    {
    return iDbAlbum->GetAlbumsCountForArtistL(aArtistId);
    }

TBool CMPXDbHandler::HandleIsUnknownArtistL(TUint32 aArtistId)
    {
    return iDbArtist->IsUnknownArtistL(aArtistId);
    }

// ---------------------------------------------------------------------------
// CMPXDbHandler::HandleArtistForAlbumL
// ---------------------------------------------------------------------------
//
HBufC* CMPXDbHandler::HandleArtistForAlbumL(const TUint32 aAlbumId)
    {

    TUint32 artistId = iDbMusic->ArtistForAlbumL(aAlbumId);
    HBufC* artistname = GetNameMatchingIdL(artistId);
    return artistname;
    }

// ---------------------------------------------------------------------------
// CMPXDbHandler::HandleAlbumartForAlbumL
// ---------------------------------------------------------------------------
//
HBufC*  CMPXDbHandler::HandleAlbumartForAlbumL(const TUint32 aAlbumId, TPtrC aArt)
    {
    return iDbMusic->AlbumartForAlbumL(aAlbumId, aArt);
    }
#ifdef ABSTRACTAUDIOALBUM_INCLUDED   
// ----------------------------------------------------------------------------------------------------------
// CMPXDbHandler::HandleGetAlbumNameForSongL
// ----------------------------------------------------------------------------------------------------------
//
HBufC* CMPXDbHandler::HandleGetAlbumNameFromIdL(TUint32 aId)
    {
    return iDbAbstractAlbum->GetUriL(aId);
    }
#endif // ABSTRACTAUDIOALBUM_INCLUDED
// End of file
