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

/*
 * Copyright (c) 2005-2006 Nokia Corporation and/or its subsidiary(-ies). 
 * All rights reserved.
 * This component and the accompanying materials are made available
 * under the terms of "Eclipse Public License v1.0"
 * which accompanies this distribution, and is available
 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 *
 * Initial Contributors:
 * Nokia Corporation - initial contribution.
 *
 * Contributors:
 *
 * Description:   starts update sequence
 *
 */

// INCLUDE FILES
#include <StringLoader.h>
#include <centralrepository.h>
#include <AknUtils.h>
#include <AknBidiTextUtils.h> 
#include <biditext.h>
#include <gdi.h>
#include <fotaengine.h>
#include <apgtask.h>
#include <aknradiobuttonsettingpage.h> 
#include <akntitle.h>
#include <schtime.h>
#include <csch_cli.h>
#include <fotaengine.h>
#include <XQConversions>
#include "fotaupdate.h"
#include "fmsclient.h"
#include "FotasrvSession.h"
#include "fotaserverPrivateCRKeys.h"
#include "fotaserverPrivatePSKeys.h"
#include "fotanotifiers.h"

#include <usbman.h>
#include <usbstates.h>
// ============================= MEMBER FUNCTIONS ============================


// ---------------------------------------------------------------------------
// CFotaUpdate::CFotaUpdate()
// ---------------------------------------------------------------------------
//
CFotaUpdate::CFotaUpdate() :
    CActive(EPriorityNormal)
    {
    CActiveScheduler::Add(this);
    }

// ---------------------------------------------------------------------------
// CFotaUpdate::~CFotaUpdate()
// ---------------------------------------------------------------------------
//
CFotaUpdate::~CFotaUpdate()
    {
    FLOG(_L("CFotaUpdate::~CFotaUpdate >>"));
    iFotaServer = NULL;

    CancelMonitor();

    if (iProperty.Handle())
        iProperty.Close();

    FLOG(_L("CFotaUpdate::~CFotaUpdate <<"));
    }

// ---------------------------------------------------------------------------
// CFotaUpdate::NewL 
// ---------------------------------------------------------------------------
//
CFotaUpdate* CFotaUpdate::NewL(CFotaServer* aServer)
    {
    CFotaUpdate* ao = new (ELeave) CFotaUpdate();
    ao->iFotaServer = aServer;
    return ao;
    }

// ---------------------------------------------------------------------------
// CFotaUpdate::CheckUpdateResults
// Checks if there is update result file available (meaning that update just
// took place)
// ---------------------------------------------------------------------------
//
TBool CFotaUpdate::CheckUpdateResults(RFs& aRfs)
    {
    RFile f;
    TInt err;
    err = f.Open(aRfs, KUpdateResultFile, EFileShareAny);
    f.Close();
    if (err != KErrNone)
        {
        return EFalse;
        }
    FLOG(_L(" CFotaUpdate::CheckUpdateResults  update result file Found! "));
    return ETrue;
    }

// ---------------------------------------------------------------------------
// CFotaUpdate::ExecuteUpdateResultFileL
// Read result code from update result file and update state accordingly. 
// Show notifier "Update succesful". Do some cleanup.
// ---------------------------------------------------------------------------
//
void CFotaUpdate::ExecuteUpdateResultFileL()
    {
    FLOG(_L("CFotaUpdate::ExecuteUpdateResultFileL >>"));

    TInt err;
    RFileReadStream rstr;
    TInt result;
    TInt msglen;
    HBufC8* message = NULL;
    HBufC16* message16 = NULL;
    TBool deleteData(EFalse);

    // Open update result file
    FLOG(_L("opening rstr 1/2  "));
    err = rstr.Open(iFotaServer->iFs, KUpdateResultFile, EFileRead
            | EFileStream);
    FLOG(_L("opened  rstr 2/2  "));
    if (err)
    	{
        FLOG(_L("   update result file open err %d"), err);
      }
    User::LeaveIfError(err);
    FLOG(_L("       0.1"));
    CleanupClosePushL(rstr);
    FLOG(_L("       0.2"));

    // Read resultcode 
    result = rstr.ReadUint32L();
    msglen = rstr.ReadUint32L();
    if (msglen != 0)
        {
        message = HBufC8::NewLC(msglen + 1);
        TPtr8 ptrdesc = message->Des();
        TRAPD ( err2, rstr.ReadL(ptrdesc) );
        if (err2 != KErrNone && err2 != KErrEof)
            {
            FLOG(_L("  file read err %d"), err2);
            User::Leave(err2);
            }

        message16 = HBufC16::NewLC(message->Des().Length());
        message16->Des().Copy(*message);
        FLOG(_L("   1 update result: %d"), result);
        FLOG(_L("   2 dbg msg: %S"), message16);
        CleanupStack::PopAndDestroy(message16);
        CleanupStack::PopAndDestroy(message);
        }

    FLOG(_L("       0.6 "));
    CleanupStack::PopAndDestroy(&rstr);

    // Map resultcode to FUMO result code
    RFotaEngineSession::TState fstate = RFotaEngineSession::EUpdateFailed;
    RFotaEngineSession::TResult fresult =
            RFotaEngineSession::EResUpdateFailed;

    FLOG(_L("   3"));
    switch (result)
        {
        case UPD_OK:
            {
            fstate = RFotaEngineSession::EUpdateSuccessfulNoData;
            fresult = RFotaEngineSession::EResSuccessful;
            deleteData = ETrue;
            ShowDialogL(EFwUpdSuccess);
            }
            break;
        case UPD_INSTALL_REQUEST_IS_INVALID:
            {
            fstate = RFotaEngineSession::EUpdateFailedNoData;
            fresult = RFotaEngineSession::EResUpdateFailed;
            deleteData = ETrue;
            ShowDialogL(EFwUpdNotCompatible);
            }
            break;
        case UPD_UPDATE_PACKAGE_IS_ABSENT:
            {
            fstate = RFotaEngineSession::EUpdateFailedNoData;
            fresult = RFotaEngineSession::EResUpdateFailed;
            ShowDialogL(EFwUpdNotCompatible);
            }
            break;
        case UPD_UPDATE_PACKAGE_IS_CORRUPTED:
            {
            fstate = RFotaEngineSession::EUpdateFailedNoData;
            fresult = RFotaEngineSession::EResCorruptedFWUPD;
            deleteData = ETrue;
            ShowDialogL(EFwUpdNotCompatible);
            }
            break;
        case UPD_UPDATE_PACKAGE_CONTENTS_IS_INVALID:
            {
            fstate = RFotaEngineSession::EUpdateFailedNoData;
            fresult = RFotaEngineSession::EResCorruptedFWUPD;
            deleteData = ETrue;
            ShowDialogL(EFwUpdNotCompatible);
            }
            break;
        case UPD_UPDATE_PACKAGE_IS_NOT_COMPATIBLE_WITH_CURRENT_MOBILE_DEVICE:
            {
            fstate = RFotaEngineSession::EUpdateFailedNoData;
            fresult = RFotaEngineSession::EResPackageMismatch;
            deleteData = ETrue;
            ShowDialogL(EFwUpdNotCompatible);
            }
            break;
        case UPD_FATAL_ERROR:
            {
            fstate = RFotaEngineSession::EUpdateFailedNoData;
            fresult = RFotaEngineSession::EResUpdateFailed;
            deleteData = ETrue;
            ShowDialogL(EFwUpdNotCompatible);
            }
            break;

        default:
            {
            FLOG(_L("   3.1 invalid result: %d"), result);
            fstate = RFotaEngineSession::EUpdateFailedNoData;
            fresult = RFotaEngineSession::EResUpdateFailed;
            deleteData = ETrue;
            ShowDialogL(EFwUpdNotCompatible);
            }
            break;
        }

    // Find the state 60 (update  progressing) -> 100 (etc)
    RArray<TInt> states;
    TPackageState state;
    CleanupClosePushL(states);
    iFotaServer->iDatabase->OpenDBL();
    iFotaServer->iDatabase->GetAllL(states);

    FLOG(_L("   4.1 found %d states "), states.Count());
    for (TInt i = 0; i < states.Count(); ++i)
        {
        TPackageState tmp;
        TBuf8<KMaxFileName> tmpurl;
        tmp = iFotaServer->iDatabase->GetStateL(states[i], tmpurl);
        FLOG(_L("   5 got state "));
        if (tmp.iState == RFotaEngineSession::EUpdateProgressing)
            {
            state = tmp;
            state.iState = fstate;
            state.iResult = fresult;
            FLOG(_L("   6 Updating state id %d  to %d ,result %d  "),
                    state.iPkgId, state.iState, state.iResult);
            iFotaServer->iDatabase->SetStateL(state, KNullDesC8, EFDBState
                    | EFDBResult);
            }
        }
    iFotaServer->iDatabase->CloseAndCommitDB();
    CleanupStack::PopAndDestroy(&states);

    // Delete request file
    err = BaflUtils::DeleteFile(iFotaServer->iFs, KUpdateRequestFile);
    if (err != KErrNone && err != KErrNotFound)
        {
        FLOG(_L("   6.1  req file deleted, err %d"), err);
        User::Leave(err);
        }

    // Write timestamp (shown to user in device management ui)
    if (fresult == RFotaEngineSession::EResSuccessful)
        {
        RFileWriteStream wstr;
        TTime time;
        User::LeaveIfError(wstr.Replace(iFotaServer->iFs,
                KUpdateTimeStampFileName, EFileWrite));
        CleanupClosePushL(wstr);
        time.HomeTime();
        TInt year = time.DateTime().Year();
        TInt month = time.DateTime().Month();
        TInt day = time.DateTime().Day();
        TInt hour = time.DateTime().Hour();
        TInt minute = time.DateTime().Minute();
        wstr.WriteInt32L(year);
        wstr.WriteInt32L(month);
        wstr.WriteInt32L(day);
        wstr.WriteInt32L(hour);
        wstr.WriteInt32L(minute);
        CleanupStack::PopAndDestroy(1); // wstr
        }
    // Delete package content
    if (deleteData && state.iPkgId > KErrNotFound)
        {
        iFotaServer->DeletePackageL(state.iPkgId);
        DeleteUpdateResultFileL();
        }

    // Report state back to syncml server
    if (state.iPkgId >= 0)
        {
        FLOG(_L("   6.1 creating device mgmt session for profile %d"),
                state.iProfileId);
        iFotaServer->ReportFwUpdateStatusL(state);
        }
    else
        {
        FLOG(_L(" No state found in 'update progress' mode! cannot report status to DM server "));
        }

    FLOG(_L("CFotaUpdate::ExecuteUpdateResultFileL <<"));
    }

// ---------------------------------------------------------------------------
// CFotaUpdate::DeleteUpdateResultsL
// ---------------------------------------------------------------------------
//
void CFotaUpdate::DeleteUpdateResultFileL()
    {
    FLOG(_L("CFotaUpdate::DeleteUpdateResultsL >>"));
    // Delete result file
    RFs fs;
    __LEAVE_IF_ERROR(fs.Connect());
    BaflUtils::DeleteFile(fs, KUpdateResultFile);
    BaflUtils::DeleteFile(fs, KUpdateRequestFile);
    BaflUtils::DeleteFile(fs, KUpdateBitmap);
    BaflUtils::DeleteFile(fs, KRestartingBitmap);
    fs.Close();
    FLOG(_L("CFotaUpdate::DeleteUpdateResultsL <<"));
    }

// ---------------------------------------------------------------------------
// CFotaUpdate::UpdateL
// Updates the fw: Creates input files for update agent and boots device to 
// update mode.
// ---------------------------------------------------------------------------
void CFotaUpdate::UpdateL()
    {
    FLOG(_L("CFotaUpdate::UpdateL() >>"));
    // Set state ........................................
    iFotaServer->iPackageState.iState
            = RFotaEngineSession::EUpdateProgressing;
    iFotaServer->iDatabase->OpenDBL();
    iFotaServer->iDatabase->SetStateL(iFotaServer->iPackageState, KNullDesC8,
            EFDBState);
    iFotaServer->iDatabase->CloseAndCommitDB();

    // Write update request for update agent..............
    FLOG(_L("CFotaUpdate::UpdateL  1 writing update.req "));
    TBuf16<KMaxFileName> dp2filepath;
    HBufC16* dp2;
    RFileWriteStream wstr;
    CleanupClosePushL(wstr);
    FLOG(_L("CFotaUpdate::UpdateL  2 getting pkg location"));
    iFotaServer->DownloaderL()->GetUpdatePackageLocation(dp2filepath);

    FLOG(_L("CFotaUpdate::UpdateL  3 craeting update.req"));
    User::LeaveIfError(wstr.Replace(iFotaServer->iFs, KUpdateRequestFile,
            EFileWrite));
    wstr.WriteInt16L(1); // version number is  1
    wstr.WriteInt32L(1); // count   of cmds is 1
    wstr.WriteInt16L(0); // requestid is 0

    dp2 = HBufC16::NewLC(dp2filepath.Length());
    dp2->Des().Copy(dp2filepath);
    wstr.WriteInt32L(dp2->Des().Length() + 1); // length of filename + null
    wstr.WriteL(dp2->Des());
    wstr.WriteInt16L(0); // null character
    CleanupStack::PopAndDestroy(dp2);
    CleanupStack::PopAndDestroy(&wstr); // wstr

    FLOG(_L("CFotaUpdate::UpdateL  4 craeting update.bmp"));


    // Write update graphic for update agent ...............
    HBufC* updatetxt;
    HBufC* restarttxt;
    QString installstr = hbTrId("txt_device_update_info_installing");
    QString rebootstr = hbTrId("txt_device_update_info_rebooting");
    updatetxt = XQConversions::qStringToS60Desc(installstr);
    restarttxt = XQConversions::qStringToS60Desc(rebootstr);
    
    WriteUpdateBitmapL( updatetxt->Des(), KUpdateBitmap );
    WriteUpdateBitmapL( restarttxt->Des(), KRestartingBitmap );
    delete updatetxt;
    delete restarttxt;
    
    // Simulate update agent by writing result file.
    CRepository* centrep(NULL);
    TInt err = KErrNone;
    TRAP(err, centrep = CRepository::NewL( KCRUidFotaServer ) );
    TInt simulate(KErrNotFound);
    if (centrep)
        {
        err = centrep->Get(KSimulateUpdateAgent, simulate);
        }
    delete centrep;
    if (simulate > 0)
        {
        FLOG(_L("CFotaUpdate::UpdateL  5  - writing update.resp"));
        RFileWriteStream respstr;
        CleanupClosePushL(respstr);
        User::LeaveIfError(respstr.Replace(iFotaServer->iFs,
                KUpdateResultFile, EFileWrite));

        respstr.WriteUint32L(UPD_OK);
        respstr.WriteUint32L(15);
        respstr.WriteL(_L("UPDATE DONE!"));
        CleanupStack::PopAndDestroy(&respstr);
        }

    iFotaServer->SetStartupReason(EFotaDefault);
    
    RStarterSession starter;
    FLOG(_L("           starter->Connect"));
    User::LeaveIfError(starter.Connect());
    starter.Reset(RStarterSession::EFirmwareUpdate);
    starter.Close();
    FLOG(_L("CFotaUpdate::UpdateL() <<"));
    }

// ---------------------------------------------------------------------------
// CFotaUpdate::CheckBatteryL()
// Cheks if there's enough battery power to update
// ---------------------------------------------------------------------------
//
TBool CFotaUpdate::CheckBatteryL()
    {
    FLOG(_L("CFotaUpdate::CheckBatteryL >>"));

    TInt chargingstatus(EChargingStatusError);
    TInt batterylevel(EBatteryLevelUnknown);
    TBool enoughPower(EFalse);

    // Read battery level

    RProperty pw;
    User::LeaveIfError(pw.Attach(KPSUidHWRMPowerState, KHWRMBatteryLevel));
    User::LeaveIfError(pw.Get(batterylevel));

    if (batterylevel >= BatteryLevelAccepted)
        {
        FLOG(_L("Battery charge is above acceptable level!"));
        enoughPower = ETrue;
        }
    else
        {

        //Read charger status
        User::LeaveIfError(pw.Attach(KPSUidHWRMPowerState,
                KHWRMChargingStatus));
        User::LeaveIfError(pw.Get(chargingstatus));
        pw.Close();

        // But charger is connected, power sufficient
        if (chargingstatus == EChargingStatusCharging || chargingstatus
                == EChargingStatusAlmostComplete || chargingstatus
                == EChargingStatusChargingComplete || chargingstatus
                == EChargingStatusChargingContinued)
            {
            FLOG(
                    _L("Battery charge is below acceptable level, but charger is connected!"));
            enoughPower = ETrue;
            }
        }

    FLOG(_L("CFotaUpdate::CheckBattery, current charge = %d, ret = %d <<"),
            batterylevel, enoughPower);
    return enoughPower;
    }

void CFotaUpdate::MonitorBatteryChargeLevel()
    {
    FLOG(_L("CFotaUpdate::MonitorBatteryChargeLevel >>"));

    CancelMonitor();

    iProperty.Attach(KPSUidHWRMPowerState, KHWRMBatteryLevel);
    TInt value = 0;

    iProperty.Get(KPSUidHWRMPowerState, KHWRMBatteryLevel, value);

    FLOG(_L("Battery level at this time is %d, err = %d"), value);
    iStatus = KRequestPending;
    iProperty.Subscribe(iStatus);

    iMonitorType = BatteryLevel;
    SetActive();

    FLOG(_L("CFotaUpdate::MonitorBatteryChargeLevel <<"));
    }

void CFotaUpdate::MonitorBatteryChargingStatus()
    {
    FLOG(_L("CFotaUpdate::MonitorBatteryChargingStatus >>"));

    CancelMonitor();

    iProperty.Attach(KPSUidHWRMPowerState, KHWRMChargingStatus);
    TInt value = 0;

    iProperty.Get(KPSUidHWRMPowerState, KHWRMChargingStatus, value);

    FLOG(_L("Charging status at this time is %d, err = %d"), value);
    iStatus = KRequestPending;
    iProperty.Subscribe(iStatus);

    iMonitorType = ChargingStatus;
    SetActive();

    FLOG(_L("CFotaUpdate::MonitorBatteryChargingStatus <<"));
    }

void CFotaUpdate::CancelMonitor()
    {
    FLOG(_L("CFotaUpdate::CancelMonitor >>"));
    if (IsActive())
        {
        FLOG(_L("Cancelling...."));
        Cancel();
        }
    FLOG(_L("CFotaUpdate::CancelMonitor <<"));
    }

TBool CFotaUpdate::IsUSBConnected()
    {
    FLOG(_L("CFotaUpdate::IsUSBConnected >>"));
    
    TBool ret (EFalse);
    TUsbDeviceState state(EUsbDeviceStateUndefined);
    RUsb usbman;
    TInt err = usbman.Connect();
    if (err == KErrNone)
        {
        usbman.GetDeviceState(state);
        usbman.Close();
        }
    if (state != EUsbDeviceStateUndefined)
    	{
        ret = ETrue;
      }
    
    FLOG(_L("CFotaUpdate::IsUSBConnected, value = %d, ret = %d <<"), state, ret);
    return ret;
    }

void CFotaUpdate::RunL()
    {
    FLOG(_L("CFotaUpdate::RunL >>"));

    if (iStatus.Int() == KErrNone)
        {
        TInt value = 0;

        if (iMonitorType == BatteryLevel)
            {
            iProperty.Get(KPSUidHWRMPowerState, KHWRMBatteryLevel,
                    value);

            FLOG(_L("Battery level has changed to %d"), value);

            iFotaServer->UpdateBatteryLowInfo(
                    (value >= BatteryLevelAccepted) ? EFalse : ETrue);

            MonitorBatteryChargeLevel();
            }
        else if (iMonitorType == ChargingStatus)
            {
            iProperty.Get(KPSUidHWRMPowerState, KHWRMChargingStatus,
                    value);

            FLOG(_L("Charging status has changed to %d"), value);

            if (value == EChargingStatusCharging || value
                    == EChargingStatusAlmostComplete || value
                    == EChargingStatusChargingComplete || value
                    == EChargingStatusChargingContinued)
                {
                if (IsUSBConnected())
                    {
                    ShowDialogL(EFwUpdNotEnoughBattery);
                    }
                else
                    {
                    iFotaServer->UpdateBatteryLowInfo(EFalse);
                    }
                }
            else
                {
                iFotaServer->UpdateBatteryLowInfo(ETrue);
                }
            }

        }
    FLOG(_L("CFotaUpdate::RunL, iStatus = %d <<"), iStatus.Int());
    }

// ---------------------------------------------------------------------------
// CFotaUpdate::StartUpdateL
// Starts fw updating (shows a notifier to user). 
// ---------------------------------------------------------------------------
//
void CFotaUpdate::StartUpdateL(const TDownloadIPCParams &aParams)
    {
    FLOG(_L("CFotaUpdate::StartUpdateL,  pkig:%d >>"), aParams.iPkgId);

    //First cancel any ongoing battery monitoring.
    CancelMonitor();

    UpdateL();

    FLOG(_L("CFotaUpdate::StartUpdateL <<"));
    }

TInt CFotaUpdate::RunError(TInt aError)
    {
    FLOG(_L("CFotaUpdate::RunError, error = %d >>"), aError);
    return aError;
    }

void CFotaUpdate::DoCancel()
    {
    FLOG(_L("CFotaUpdate::DoCancel >>"));

    iProperty.Cancel();

    FLOG(_L("CFotaUpdate::DoCancel <<"));
    }

void CFotaUpdate::ShowDialogL(TFwUpdNoteTypes aDialogid)
    {
    FLOG(_L("CFotaUpdate::ShowDialogL, dialogid = %d >>"), aDialogid);
    iFotaServer->ServerCanShut(EFalse);

    if (iFotaServer->FullScreenDialog())
        iFotaServer->FullScreenDialog()->Close();

    iNotifParams = CHbSymbianVariantMap::NewL();

    HBufC* keyDialog = HBufC::NewL(10);
    CleanupStack::PushL(keyDialog);
    *keyDialog = KKeyDialog;

    CHbSymbianVariant* dialogId = CHbSymbianVariant::NewL(&aDialogid,
            CHbSymbianVariant::EInt);
    iNotifParams->Add(*keyDialog, dialogId);
    iNotifier = CFotaDownloadNotifHandler::NewL(this);

    iNotifier->LaunchNotifierL(iNotifParams, aDialogid);

    CleanupStack::PopAndDestroy();
    FLOG(_L("CFotaUpdate::ShowDialogL <<"));
    }

void CFotaUpdate::HandleDialogResponse(int response, TInt aDialogId)
    {
    FLOG(_L("CFotaUpdate::HandleDialogResponse, dialogid = %d response = %d >>"), aDialogId, response);

    if (aDialogId == EFwUpdNotEnoughBattery)
        {
        iFotaServer->FinalizeUpdate();
        }

    FLOG(_L("CFotaUpdate::HandleDialogResponse <<"));
    }

TInt CFotaUpdate::WriteUpdateBitmapL( const TDesC& aText, const TDesC& aFile)
    {
    FLOG(_L("WriteUpdateBitmapL writing %S to %S w/ txtdir"),&aText,&aFile);

    TSize   screensize = CCoeEnv::Static()->ScreenDevice()->SizeInPixels();
    TInt                width  = screensize.iWidth - KBmpMargin*2;
    TInt                height =  screensize.iHeight;

    CArrayFixSeg<TPtrC>*   lines = new CArrayFixSeg<TPtrC>(5);
    CleanupStack::PushL(lines);
    CFbsBitmap*         bitmap = new ( ELeave ) CFbsBitmap;
    CleanupStack::PushL( bitmap );
    bitmap->Create(  TSize(width,height), EColor64K );
    CFbsBitmapDevice*   device = CFbsBitmapDevice::NewL( bitmap );
    CleanupStack::PushL( device );
    const CFont* font = AknLayoutUtils::FontFromId(EAknLogicalFontPrimaryFont);
    CFbsBitGc*          context;
    User::LeaveIfError( device->CreateContext( context ) );
    CleanupStack::PushL( context );
    TInt                ascent = font->AscentInPixels();
    TInt                descent = font->DescentInPixels();
    context->UseFont ( font );
    context->Clear(); // bg color

    // Visually ordered text
    HBufC* wrappedstring = AknBidiTextUtils::ConvertToVisualAndWrapToArrayL(
            aText, width,*font, *lines);
    CleanupStack::PushL ( wrappedstring );
    TBool dirfound (ETrue);
    // direction of text, affects alignemnt
    TBidiText::TDirectionality direction = TBidiText::TextDirectionality(
            *wrappedstring, &dirfound );

    // Print visual text to bitmap
    for ( TInt i=0; i<lines->Count(); ++i ) 
        {
        TPtrC l = (*lines)[i];
        TInt top = (ascent+descent)*(i);
        TInt bot = (ascent+descent)*(i+1);
        TRect rect (0, top ,width, bot );
        CGraphicsContext::TTextAlign alignment = 
        direction==TBidiText::ELeftToRight ? CGraphicsContext::ELeft 
            : CGraphicsContext::ERight;
        context->DrawText(l, rect, ascent, alignment);
        }
    height = (ascent+descent)*lines->Count() + descent;
    bitmap->Resize( TSize(width,height));
    bitmap->Save( aFile );
    context->DiscardFont();
    CleanupStack::PopAndDestroy( wrappedstring );
    CleanupStack::PopAndDestroy( context );
    CleanupStack::PopAndDestroy( device );
    CleanupStack::PopAndDestroy( bitmap );
    CleanupStack::PopAndDestroy( lines );
    return 1;
    }