fotaapplication/fotaserver/src/FotaDB.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 16:04:06 +0300
branchRCL_3
changeset 61 b183ec05bd8c
permissions -rw-r--r--
Revision: 201033 Kit: 201035

/*
 * Copyright (c) 2005 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:   Fota package state preservation 
 *
 */

#include <centralrepository.h>
#include "FotaDB.h"
#include "FotaSrvDebug.h"
#include "fotaserverPrivateCRKeys.h"

#define __LEAVE_IF_ERROR(x) if(KErrNone!=x) {FLOG(_L("LEAVE in %s: %d"), __FILE__, __LINE__); User::Leave(x); }

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

// ---------------------------------------------------------------------------
// CFotaDB::CFotaDB()
// ---------------------------------------------------------------------------
CFotaDB::CFotaDB() :
    iIsOpen(EFalse)
    {
    }

// ---------------------------------------------------------------------------
// CFotaDB::~CFotaDB()
// ---------------------------------------------------------------------------
CFotaDB::~CFotaDB()
    {
    //Delete columns set
    if (iColSet)
        {
        delete iColSet;
        iColSet = NULL;
        }
    //Close table
    iTable.Close();

    //Close database
    iStateDB.Close();

    //Close file server session
    if (iFSSession.Handle())
        iFSSession.Close();
    }

// ---------------------------------------------------------------------------
// CFotaDB::NewL()
// ---------------------------------------------------------------------------
CFotaDB* CFotaDB::NewL()
    {
    CFotaDB* self = new (ELeave) CFotaDB();
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

// ---------------------------------------------------------------------------
// CFotaDB::ConstructL()
// ---------------------------------------------------------------------------
void CFotaDB::ConstructL()
    {
    TInt err;
    TBuf<KMaxPath> tmp;
    User::LeaveIfError(iFSSession.Connect());
    err = iFSSession.CreatePrivatePath(EDriveC);
    if (err != KErrNone && err != KErrAlreadyExists)
        User::Leave(err);
    User::LeaveIfError(iFSSession.SetSessionToPrivate(EDriveC));
    User::LeaveIfError(iFSSession.SessionPath(tmp));
    }

// ---------------------------------------------------------------------------
// CFotaDB::CreateDatabaseL()
// Creates db in private dir of fotaserver
// ---------------------------------------------------------------------------
void CFotaDB::CreateDatabaseL()
    {
    FLOG(_L("CFotaDB::CreateDatabaseL()  >>"));
    TInt err;
    User::LeaveIfError(iStateDB.Create(iFSSession, KDatabaseName));
    User::LeaveIfError(iStateDB.Begin());
    User::LeaveIfError(iStateDB.Execute(KCreateTable));
    err = iStateDB.Commit();
    if (err)
        {
        FLOG(_L("      cdb err %d"), err);
        iStateDB.Rollback();
        User::Leave(err);
        }
    User::LeaveIfError(iStateDB.Compact());
    FLOG(_L("CFotaDB::CreateDatabaseL()  <<"));
    }

// ---------------------------------------------------------------------------
// CFotaDB::AddPackageStateL
// Adds state to db
// ---------------------------------------------------------------------------
void CFotaDB::AddPackageStateL(const TPackageState& aState,
        const TDesC8& aPkgURL)
    {
    FLOG(_L("  CFotaDB::AddPackageStateL   >>"));
    TInt err;
    RDbView view;
    CleanupClosePushL(view);
    TPackageState pkgstate(aState);
    pkgstate.iResult = -1; // result should be -1 if no Execs have been done
    err = view.Prepare(iStateDB, TDbQuery(KSelectAll), RDbView::EInsertOnly);
    __LEAVE_IF_ERROR(err);
    FLOG(_L("  CFotaDB::AddPackageStateL   inserting. pkgid:%d result:%d  state:%d"),
            pkgstate.iPkgId, pkgstate.iResult, pkgstate.iState);
    view.InsertL();
    StateToRowL(pkgstate, aPkgURL, view);
    view.PutL();
    CleanupStack::PopAndDestroy(); //view
    FLOG(_L("  CFotaDB::AddPackageStateL   <<"));
    }

// ---------------------------------------------------------------------------
// CFotaDB::GetAllL
// Get all states
// ---------------------------------------------------------------------------
void CFotaDB::GetAllL(RArray<TInt>& aStates)
    {
    RDbView view;
    CleanupClosePushL(view);

    TInt err = view.Prepare(iStateDB, TDbQuery(KSelectAll));
    __LEAVE_IF_ERROR(err);
    view.EvaluateAll();

    view.FirstL();
    FLOG(_L("[fota DB] --- rows ------------------------------------------------------------------- v"));
    while (view.AtRow())
        {
        view.GetL();
        HBufC8* url;
        TPackageState s = RowToStateL(url, view);
        aStates.Append(s.iPkgId);
        CleanupStack::PushL(url);
        FLOG(_L("[fotaDB] pkgid: %d profid:%d state:%d  result:%d \
                url: %d chars sessiontype:%d IapId:%d Pkgsize:%d UpdateLtr:%d"),
                s.iPkgId, s.iProfileId, s.iState, s.iResult,
                url->Des().Length(), s.iSessionType, s.iIapId, s.iPkgSize,
                s.iUpdateLtr);
        CleanupStack::PopAndDestroy(); // url
        view.NextL();
        }
    FLOG(_L("[fota DB] --- rows ------------------------------------------------------------------- ^"));
    view.Close();
    CleanupStack::PopAndDestroy(); //view
    }

// ---------------------------------------------------------------------------
// CFotaDB::OpenDBL()
// Opens database
// ---------------------------------------------------------------------------
void CFotaDB::OpenDBL()
    {
    FLOG(_L("CFotaDB::OpenDBL()"));
    if (!iIsOpen) //Prevents accidental opening of database twice
        {
        TInt err;
        err = iStateDB.Open(iFSSession, KDatabaseName);

        if (err == KErrNotFound)
            {
            CreateDatabaseL();
            }
        else if (err != KErrNone)
            {
            FLOG(_L("[fota DB openDB]\t db open error: %d"), err);
            FLOG(_L("deleting fota DB and creating it again..."));
            err = iFSSession.Delete(KDatabaseName);
            CreateDatabaseL();
            User::LeaveIfError(err);
            }
        User::LeaveIfError(iTable.Open(iStateDB, KTblState));
        iColSet = iTable.ColSetL();

        iIsOpen = ETrue;
        }
    }

// ---------------------------------------------------------------------------
// CFotaDB::CloseAndCommitDB
// Closes and commits DB
// ---------------------------------------------------------------------------
void CFotaDB::CloseAndCommitDB()
    {
    if (iColSet)
        {
        delete iColSet;
        iColSet = NULL;
        }
    iTable.Close();
    iStateDB.Close();
    iIsOpen = EFalse;
    }

// ---------------------------------------------------------------------------
// CFotaDB::IsOpen()
// Chekcs if db is open
// ---------------------------------------------------------------------------
TBool CFotaDB::IsOpen()
    {
    return iIsOpen;
    }

// ---------------------------------------------------------------------------
// CFotaDB::GetStateL
// Gets pkg state from db
// ---------------------------------------------------------------------------
TPackageState CFotaDB::GetStateL(const TInt aPkgId, TDes8& aPkgURL)
    {
    RDbView view;
    TPackageState s;
    CleanupClosePushL(view);

    s.iState = RFotaEngineSession::EIdle; // default state is idle

    HBufC* select = HBufC::NewLC(KSelect_where_packageid().Length() + 10);
    select->Des().Format(KSelect_where_packageid, aPkgId);

    TInt err = view.Prepare(iStateDB, TDbQuery(*select));
    __LEAVE_IF_ERROR(err);
    view.EvaluateAll();
    view.FirstL();

    while (view.AtRow())
        {
        view.GetL();
        HBufC8* url;
        s = RowToStateL(url, view);
        CleanupStack::PushL(url);
        aPkgURL.Copy(url->Des());
        CleanupStack::PopAndDestroy(); // url
        view.NextL();
        }
    CleanupStack::PopAndDestroy(); //select
    CleanupStack::PopAndDestroy(); //view
    return s;
    }

// ---------------------------------------------------------------------------
// CFotaDB::SetStateL
// Writes package state to DB. 
// ---------------------------------------------------------------------------
void CFotaDB::SetStateL(TPackageState& aState, const TDesC8& aPkgURL,
        TUint aChangedFields)
    {
    FLOG(_L("  CFotaDB::SetStateL  >>  id %d result %d  state %d sessiontype %d iapid %d pkgsize %d updateltr %d"), aState.iPkgId, aState.iResult, aState.iState);
    __ASSERT_ALWAYS( aChangedFields!=0, User::Leave(KErrArgument) );
    TBuf8<KMaxFileName> temp;
    TPackageState found = GetStateL(aState.iPkgId, temp);
    if (found.iPkgId == KErrNotFound)
        {
        AddPackageStateL(aState, aPkgURL);
        }
    else
        {
        // sml try count must be reset, if state is set
        if (aChangedFields & EFDBState)
            {
            aChangedFields = aChangedFields | EFDBSmlTryCount;
            SetRetryCount(aState);
            }

        // Construct a SQL string for update. 
        // Example: UPDATE State SET Result=40,State=4 WHERE pkgID=5
        // 
        TInt sqlsize = 0;
        _LIT8( KSqlbegin, "UPDATE State SET " );
        TBuf<21> sqlEnd;
        HBufC8* sql(NULL);
        // determine characters needed 
        sqlsize = DetermineCharNeeded(aChangedFields, aState, aPkgURL);
        sqlEnd.AppendFormat(_L(" WHERE pkgID=%d"), aState.iPkgId);

        sql = HBufC8::NewLC(((TDesC8) KSqlbegin).Length() + sqlsize
                + sqlEnd.Length());

        sql->Des().Append(KSqlbegin);

        if (aChangedFields & EFDBResult)
            {
            // check FUMO compability
            __ASSERT_ALWAYS( aState.iResult>=KErrNotFound
                    && aState.iResult<=602, User::Leave(KErrArgument) );
            sql->Des().AppendFormat(_L8("Result=%d,"), aState.iResult);
            }
        if (aChangedFields & EFDBState)
            {
            // check FUMO compability
            __ASSERT_ALWAYS( aState.iState>=0 && aState.iState<=100
                    , User::Leave(KErrArgument) );
            sql->Des().AppendFormat(_L8("State=%d,"), aState.iState);
            }
        if (aChangedFields & EFDBProfileId)
            {
            sql->Des().AppendFormat(_L8("profileid=%d,"), aState.iProfileId);
            }
        if (aChangedFields & EFDBPkgUrl)
            {
            sql->Des().AppendFormat(_L8("pkgurl='%S',"), &aPkgURL);
            }
        if (aChangedFields & EFDBPkgName)
            {
            sql->Des().AppendFormat(_L8("pkgname='%S',"), &(aState.iPkgName));
            }
        if (aChangedFields & EFDBVersion)
            {
            sql->Des().AppendFormat(_L8("Version='%S',"),
                    &(aState.iPkgVersion));
            }
        if (aChangedFields & EFDBSmlTryCount)
            {
            __ASSERT_ALWAYS( aState.iSmlTryCount>=0
                    , User::Leave(KErrArgument) );
            sql->Des().AppendFormat(_L8("SmlTryCount=%d,"),
                    aState.iSmlTryCount);

            }
        if (aChangedFields & EFDBSessionType)
            {
            sql->Des().AppendFormat(_L8("SessionType=%d,"),
                    aState.iSessionType);
            }
        if (aChangedFields & EFDBIapId)
            {
            // validate IAP ID
            __ASSERT_ALWAYS( aState.iIapId>=-1 ,User::Leave(KErrArgument) );
            sql->Des().AppendFormat(_L8("IapId=%d,"), aState.iIapId);
            }
        if (aChangedFields & EFDBPkgSize)
            {
            sql->Des().AppendFormat(_L8("PkgSize=%d,"), aState.iPkgSize);
            }
        if (aChangedFields & EFDBUpdateLtr)
            {
            // validate bit
            sql->Des().AppendFormat(_L8("UpdateLtr=%d,"), aState.iUpdateLtr);
            }
        // remove trailing ,
        if (aChangedFields)
            {
            sql->Des().SetLength(sql->Des().Length() - 1);
            }
        sql->Des().Append(sqlEnd);
        HBufC* sql2 = HBufC::NewLC(sql->Length()); // to cleanupstack
        sql2->Des().Copy(sql->Des());
        FLOG(_L("  sql:%S"), sql2);

        User::LeaveIfError(iStateDB.Begin());
        User::LeaveIfError(iStateDB.Execute(*sql2));
        User::LeaveIfError(iStateDB.Commit());
        User::LeaveIfError(iStateDB.Compact());

        CleanupStack::PopAndDestroy(sql2); //sql2
        CleanupStack::PopAndDestroy(sql); //sql
        }
    FLOG(_L("  CFotaDB::SetStateL  <<"));
    }

// ---------------------------------------------------------------------------
// CFotaDB::DetermineCharNeeded 
// Returns the char needed fro the query 
// ---------------------------------------------------------------------------

TInt CFotaDB::DetermineCharNeeded(TInt aChangedFields, TPackageState& aState,
        const TDesC8& aPkgURL)

    {

    TInt sqlsize = 0;
    if (aChangedFields & EFDBResult)
        sqlsize += 4 + 7 + 4;
    if (aChangedFields & EFDBState)
        sqlsize += 4 + 5 + 4;
    if (aChangedFields & EFDBProfileId)
        sqlsize += 4 + 9 + 4;
    if (aChangedFields & EFDBPkgUrl)
        sqlsize += aPkgURL.Length() + 6 + 4;
    if (aChangedFields & EFDBPkgName)
        sqlsize += aState.iPkgName.Length() + 7 + 4;
    if (aChangedFields & EFDBVersion)
        sqlsize += aState.iPkgVersion.Length() + 7 + 4;
    if (aChangedFields & EFDBSmlTryCount)
        sqlsize += 4 + 11 + 4;

    if (aChangedFields & EFDBSessionType)
        sqlsize += 4 + 11 + 4;
    if (aChangedFields & EFDBIapId)
        sqlsize += 4 + 5 + 4;
    if (aChangedFields & EFDBPkgSize)
        sqlsize += 4 + 7 + 10;
    if (aChangedFields & EFDBUpdateLtr)
        sqlsize += 4 + 11 + 4;

    return sqlsize;

    }

// ---------------------------------------------------------------------------
// CFotaDB::SetRetryCount
// Sets the retry count
// ---------------------------------------------------------------------------
void CFotaDB::SetRetryCount(TPackageState& aState)
    {
    CRepository* centrep(NULL);
    TInt err = KErrNone;
    TInt retry = 0;

    TRAP(err, centrep = CRepository::NewL( KCRUidFotaServer ) );
    if (centrep)
        {
        err = centrep->Get(KGenericAlertRetries, retry);
        delete centrep;
        centrep = NULL;
        }
    if (err == KErrNone)
        {
        if (retry < 0)
            {
            aState.iSmlTryCount = KDefaultSmlTryCount;
            }
        else if (retry == 0)
            {
            aState.iSmlTryCount = 2;
            }
        else if (retry > KMaximumSmlTryCount)
            {
            aState.iSmlTryCount = KMaximumSmlTryCount + 1;
            }
        else
            {
            aState.iSmlTryCount = retry + 1;
            }
        }
    else
        {
        aState.iSmlTryCount = KDefaultSmlTryCount;
        }
    }

// ---------------------------------------------------------------------------
// CFotaDB::RowToStateL
// Extracts db row contents to package state object and aPkgUrl
// Returns url in aPkgURL parameter
// ---------------------------------------------------------------------------
TPackageState CFotaDB::RowToStateL(HBufC8*& aPkgUrl, const RDbView& aView)
    {
    TPackageState s;
    TInt pkgid = aView.ColInt(iColSet->ColNo(KColPkgId));
    TInt state = aView.ColInt(iColSet->ColNo(KColState));
    TInt result = aView.ColInt(iColSet->ColNo(KColResult));
    TSmlProfileId profileid(aView.ColInt(iColSet->ColNo(KColProfileId)));
    TPtrC pkgname = aView.ColDes(iColSet->ColNo(KColPkgName));
    TPtrC version = aView.ColDes(iColSet->ColNo(KColVersion));
    TInt smltrycount = aView.ColInt(iColSet->ColNo(KColSmlTryCount));
    TInt sessiontype = aView.ColInt(iColSet->ColNo(KColSessionType));
    TInt iapid = aView.ColInt(iColSet->ColNo(KColIapId));
    TUint pkgsize = aView.ColUint(iColSet->ColNo(KColPkgSize));
    TBool updateltr = aView.ColUint8(iColSet->ColNo(KColUpdateLtr));

    s.iPkgId = pkgid;
    s.iPkgName.Copy(pkgname);
    s.iPkgVersion.Copy(version);
    s.iProfileId = profileid;
    s.iResult = result;
    s.iState = RFotaEngineSession::TState(state);
    s.iSmlTryCount = smltrycount;
    s.iSessionType = sessiontype;
    s.iIapId = iapid;
    s.iPkgSize = pkgsize;
    s.iUpdateLtr = updateltr;

    RDbColReadStream rstream;
    TInt len = aView.ColLength(iColSet->ColNo(KColPkgUrl));
    rstream.OpenLC(aView, iColSet->ColNo(KColPkgUrl));
    HBufC* pkgurl = HBufC::NewLC(len);
    TPtr ptr = pkgurl->Des();
    rstream.ReadL(ptr, len);

    HBufC8* tmp = HBufC8::NewL(pkgurl->Des().Length());
    tmp->Des().Copy(pkgurl->Des());
    aPkgUrl = tmp;

    CleanupStack::PopAndDestroy(pkgurl);
    CleanupStack::PopAndDestroy(&rstream);
    return s;
    }

// ---------------------------------------------------------------------------
// CFotaDB::StateToRowL
// Converts state object to database row (into view object)
// ---------------------------------------------------------------------------
void CFotaDB::StateToRowL(const TPackageState& aPkg, const TDesC8& aPkgURL,
        RDbView& aView)
    {
    HBufC* pkgname = HBufC::NewLC(aPkg.iPkgName.Length());
    HBufC* version = HBufC::NewLC(aPkg.iPkgVersion.Length());

    pkgname->Des().Copy(aPkg.iPkgName);
    version->Des().Copy(aPkg.iPkgVersion);

    aView.SetColL(iColSet->ColNo(KColPkgId), aPkg.iPkgId);
    aView.SetColL(iColSet->ColNo(KColResult), aPkg.iResult);
    aView.SetColL(iColSet->ColNo(KColState), aPkg.iState);
    aView.SetColL(iColSet->ColNo(KColProfileId), aPkg.iProfileId);
    aView.SetColL(iColSet->ColNo(KColPkgName), *pkgname);
    aView.SetColL(iColSet->ColNo(KColVersion), *version);
    aView.SetColL(iColSet->ColNo(KColSmlTryCount), aPkg.iSmlTryCount);
    aView.SetColL(iColSet->ColNo(KColSessionType), aPkg.iSessionType);
    aView.SetColL(iColSet->ColNo(KColIapId), aPkg.iIapId);
    aView.SetColL(iColSet->ColNo(KColPkgSize), aPkg.iPkgSize);
    aView.SetColL(iColSet->ColNo(KColUpdateLtr), aPkg.iUpdateLtr);

    RDbColWriteStream wstream;
    CleanupClosePushL(wstream);
    wstream.OpenL(aView, iColSet->ColNo(KColPkgUrl));
    // Cannot write 8 bit descriptors to databae
    HBufC* buf = HBufC::NewLC(aPkgURL.Length());
    buf->Des().Copy(aPkgURL);
    wstream.WriteL(buf->Des());

    FLOG(_L("CFotaDB::StateToRowL  id:%d result:%d state:%d profileid:%d \
    		name:%d chars version: %d chars url: %d chars sessiontype:%d iapid:%d pkgsize:%d updateltr = %d"),
            aPkg.iPkgId, aPkg.iResult, aPkg.iState, aPkg.iProfileId,
            pkgname->Des().Length(), version->Des().Length(),
            buf->Des().Length(), aPkg.iSessionType, aPkg.iIapId,
            aPkg.iPkgSize, aPkg.iUpdateLtr);

    CleanupStack::PopAndDestroy(buf);
    CleanupStack::PopAndDestroy(&wstream);
    CleanupStack::PopAndDestroy(version);
    CleanupStack::PopAndDestroy(pkgname);
    }