javatools/javaapppreconverter/src.s60/preconverter.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 12:27:20 +0300
changeset 21 2a9601315dfc
permissions -rw-r--r--
Revision: v2.1.22 Kit: 201018

/*
* Copyright (c) 2008 - 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Java platform 2.0 javaapppreconverter process.
*              This process needs AllFiles capability to be able to read
*              the icon files of all midlets and to store midlet data
*              to the data cage of the javaappbackconverter process.
*
*/


#include <e32base.h>
#include <pathinfo.h>
#include <driveinfo.h>
#include <apgcli.h>
#include <d32dbms.h>

#include <mdatabasev2.h>
#include <mmidlettablev2.h>
#include <mmidlettableiterator.h>

#include "javacommonutils.h"
#include "javauids.h"
#include "preconverter.h"
#include "noarmlogs.h"

const TInt    KDelayWhenWaitingAppArc = 500000;
const TUint32 KDBVersion = 0x0200;

// Name for the system AMS DB policy
_LIT(KUidSSystemAMSDbPolicy, "secure[102045FE]");

// Constant needed to access SystemAMS static database
_LIT(KStaticDbFileName, "C:\\private\\100012A5\\DBS_102045FE_MIDP2SystemAMSStaticV2");

// This is in the private data cage of javaappbackconverter.
// javaappbackconverter will destroy the files created to this directory
// after it has done the back conversion.
_LIT(KMidletExportDirectory, "C:\\private\\20022D90\\data\\");

// This is in the private data cage of usersettingsconfigurator.
_LIT(KMidletExportDirectoryForUserSettings, "C:\\private\\20022E7A\\data\\");

// This is in the private data cage of javappconverter
// javaappconverter will destroy the 'uids' file created to this directory
// after it has made the conversion.
_LIT(KUidsExportDirectory, "C:\\private\\2002121C\\data\\");
_LIT(KUidsExportDataFileName, "uids");

// These literals are used for logging midlet information found from db.
_LIT8(KMLName, "\nname: ");
_LIT8(KMLClassName, "\nclassName: ");
_LIT8(KMLIdString, "\nidString: ");


/**
 * To create new instance of this class.
 *
 * @param aFs - A reference to the file server.
 * @return Reference to the object of this class.
 * @exception If construction fails.
 */
CPreConverter* CPreConverter::NewLC(RFs& aFs)
{
    CPreConverter* self = new(ELeave) CPreConverter(aFs);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
}

/**
 * To do 1st phase construction for this object.
 *
 * Adds this active object to the scheduler.
 */
CPreConverter::CPreConverter(RFs& aFs) :
        CActive(EPriorityStandard), iFs(aFs)
{
    CActiveScheduler::Add(this);
}

/**
 * To do 2nd phase construction for this object.
 *
 * @exception If the method is not able to allocate necessary buffers.
 */
void CPreConverter::ConstructL()
{
    JELOG2(EJavaConverters);

    iMidlets = new(ELeave) CMidletList();
    iState = EConversionDataAlreadyGathered;
}

/**
 * Deletes this object.
 * All allocated resources are released.
 */
CPreConverter::~CPreConverter()
{
    JELOG2(EJavaConverters);
    Cancel();
    if (iMidlets)
    {
        iMidlets->ResetAndDestroy();
        delete iMidlets;
        iMidlets = NULL;
    }
}

/**
 * To start preconversion
 */
void CPreConverter::Start()
{
    JELOG2(EJavaConverters);
    iState = EConversionDataAlreadyGathered;
    CompleteRequest();
}

/**
 * To stop whole preconversion.
 * Stops the active scheduler.
 */
void CPreConverter::Exit()
{
    Deque();
    CActiveScheduler::Stop();
}

/**
 * To complete the request for this object.
 *
 * @Postconditions The following conditions are true immediately after
 * returning from this method.
 * - iStatus == KErrNone
 * - IsActive() == ETrue
 */
void CPreConverter::CompleteRequest()
{
    JELOG2(EJavaConverters);

    TRequestStatus *status = &iStatus;
    User::RequestComplete(status, KErrNone);
    if (!IsActive())
    {
        SetActive();
    }
}

/**
 * To run this active object.
 *
 * The state logic is:
 *
 * EConversionDataAlreadyGathered:
 *    if midlet export data file already exists,
 *       active object exits from this state
 *
 * EFindOutInstalledMidlets:
 *    iterate through all apps in AppArc,
 *      if java app,
 *        store all info available from AppArc to iMidlets
 *
 * EFillDataFromSystemAmsDb:
 *    add info from SystemAMS DB to iMidlets
 *
 * EStoreData:
 *    store data in iMidlets to a file
 *
 * EStopMidlets:
 *    stop all running midlets
 *
 * EUnregisterMidlets
 *    unregister all midlets in iMidlets from AppArc
 *
 * EExit:
 *    free resources and exit
 *
 */
void CPreConverter::RunL()
{
    JELOG2(EJavaConverters);

    switch (iState)
    {

    case EConversionDataAlreadyGathered:
    {
        LOG(EJavaConverters, EInfo,
            "CPreConverter::RunL EConversionDataAlreadyGathered");

        TFileName exportDataFile(KMidletExportDirectory);
        exportDataFile.Append(KMidletExportDataFileName);
        TUint attributes;
        TInt  err = iFs.Att(exportDataFile, attributes);
        if (KErrNone == err)
        {
            LOG(EJavaConverters, EInfo,
                "CPreConverter::RunL EConversionDataAlreadyGathered "
                "data file already exists. Can exit.");
            iState = EExit;
        }
        else
        {
            if ((KErrNotFound == err) || (KErrPathNotFound == err))
            {
                // We come here only when OMJ is not currently installed
                // to the device. However, the device may still have
                // old OMJ database files. Clean them now to make sure
                // that e.g. old db schemas do not cause problems.
                LOG(EJavaConverters, EInfo,
                    "CPreConverter::RunL EConversionDataAlreadyGathered "
                    "Trying to destroy OMJ database files");

                _LIT(KJavaOtaStorageDbPath, "C:\\private\\10281E17\\[200211dc]JavaOtaStorage.db");
                err = iFs.Delete(KJavaOtaStorageDbPath);
                LOG1(EJavaConverters, EInfo,
                     "CPreConverter::RunL EConversionDataAlreadyGathered "
                     "Destroy C:\\private\\10281E17\\[200211dc]JavaOtaStorage.db, err %d", err);

                _LIT(KJavaOtaStorageJournalPath, "C:\\private\\10281E17\\[200211dc]JavaOtaStorage.db-journal");
                err = iFs.Delete(KJavaOtaStorageJournalPath);
                LOG1(EJavaConverters, EInfo,
                     "CPreConverter::RunL EConversionDataAlreadyGathered "
                     "Destroy C:\\private\\10281E17\\[200211dc]JavaOtaStorage.db-journal, err %d", err);

                _LIT(KJavaStorageDbPath, "C:\\private\\10281E17\\[200211dc]JavaStorage.db");
                err = iFs.Delete(KJavaStorageDbPath);
                LOG1(EJavaConverters, EInfo,
                     "CPreConverter::RunL EConversionDataAlreadyGathered "
                     "Destroy C:\\private\\10281E17\\[200211dc]JavaStorage.db, err %d", err);

                _LIT(KJavaStorageJournalPath, "C:\\private\\10281E17\\[200211dc]JavaStorage.db-journal");
                err = iFs.Delete(KJavaStorageJournalPath);
                LOG1(EJavaConverters, EInfo,
                     "CPreConverter::RunL EConversionDataAlreadyGathered "
                     "Destroy C:\\private\\10281E17\\[200211dc]JavaStorage.db-journal, err %d", err);

                LOG(EJavaConverters, EInfo,
                    "CPreConverter::RunL EConversionDataAlreadyGathered "
                    "Data file does not exist yet. OK");
            }
            else
            {
                LOG1(EJavaConverters, EInfo,
                     "CPreConverter::RunL EConversionDataAlreadyGathered "
                     "Checking data file attributes caused error %d", err);
            }
            iState = EFindOutInstalledMidlets;
        }
        CompleteRequest();
    }
    break;

    case EFindOutInstalledMidlets:
    {
        LOG(EJavaConverters, EInfo,
            "CPreConverter::RunL EFindOutInstalledMidlets");

        // Read all installed midlets from AppArc,
        // store info to iMidlets
        ReadMidletsFromAppArcL();

        iState = EFillDataFromSystemAmsDb;
        CompleteRequest();
    }
    break;

    case EFillDataFromSystemAmsDb:
    {
        LOG(EJavaConverters, EInfo,
            "CPreConverter::RunL EFillDataFromSystemAmsDb");

        // Some of the necessary information is in SystemAMS DB,
        // read it now for each midlet in iMidlets
        AddDataFromSystemAmsDbL();

        // Especially in R&D environment it is possible to that
        // AppArc contains some midlets that have not really
        // been installed to old S60 java environment.
        // SystemAMS DB does not contain any information of
        // these midlets. They must be removed now.
        RemoveInvalidMidlets();

        iState = EStoreData;
        CompleteRequest();
    }
    break;

    case EStoreData:
    {
        LOG(EJavaConverters, EInfo,
            "CPreConverter::RunL EStoreData");

        if (iMidlets->Count() == 0)
        {
            // No midlets to convert
            WLOG(EJavaConverters, "CPreConverter::RunL EStoreData No midlets to convert.");
        }
        else
        {
            // Store the data directly to the data cage of
            // javaappbackconverter.exe
            TFileName exportDirectory(KMidletExportDirectory);
            iMidlets->ExportListL(iFs, exportDirectory);

            // Store the data also to the data cage of
            // usersettingsconfigurator.exe
            TFileName exportDirectoryForUserSettings(KMidletExportDirectoryForUserSettings);
            iMidlets->ExportListL(iFs, exportDirectoryForUserSettings);

            // Store the uids of the midlets to be converted
            // to the data cage of javaappconverter.exe
            StoreUidsL();
        }

        iState = EStopMidlets;
        CompleteRequest();
    }
    break;

    case EStopMidlets:
    {
        LOG(EJavaConverters, EInfo,
            "CPreConverter::RunL EStopMidlets");

        CMidletInfo* pMidlet = iMidlets->GetFirst();

        TFullName foundName;
        TFullName searchName;

        while (pMidlet != NULL)
        {
            searchName = pMidlet->GetMidletName();
            _LIT(KMidletProcessIdString, "[102033e6]*");
            searchName.Append(KMidletProcessIdString);

            LOG1WSTR(EJavaConverters, EInfo, "Searching midlet process named %s",
                     (wchar_t *)(searchName.PtrZ()));

            TFindProcess find(searchName);
            TInt err = find.Next(foundName);
            if (KErrNone == err)
            {
                LOG1WSTR(EJavaConverters, EInfo, "The midlet process %s was found",
                         (wchar_t *)(foundName.PtrZ()));
                RProcess process;
                err = process.Open(find);
                if (KErrNone == err)
                {
                    LOG(EJavaConverters, EInfo, "Killing the midlet process");
                    process.Kill(KErrNone);
                    process.Close();
                }
            }

            pMidlet = iMidlets->GetNext();
        }

        iState = EUnregisterMidlets;
        CompleteRequest();
    }
    break;

    case EUnregisterMidlets:
    {
        LOG(EJavaConverters, EInfo, "CPreConverter::RunL EUnregisterMidlets");

        // Unregister all midlets in iMidlets from AppArc
        UnregisterOldJavaAppsL();

        iState = EExit;
        CompleteRequest();
    }
    break;

    case EExit:
    {
        LOG(EJavaConverters, EInfo, "CPreConverter::RunL EExit");

        FullCleanup();

        // The whole javaapppreconverter process is stopped.
        Exit();
    }
    break;

    }
}

/**
 * To handle leave from RunL.
 * This method exits this active object using normal state machine
 * After calling this method this active object will exit.
 *
 * @param aError - A reason of error.
 * @return KErrNone.
 */
TInt CPreConverter::RunError(TInt aError)
{
    ELOG2(EJavaConverters,
          "CPreConverter::RunError(%d) from state %d", aError, iState);

    Cancel();

    iState = EExit;
    CompleteRequest();

    return KErrNone;
}

/**
 * To do cancelling for this object.
 *
 */
void CPreConverter::DoCancel()
{
    ELOG1(EJavaConverters,
          "CPreConverter::DoCancel from state %d", iState);

}

/**
 * To cleanup member variables.
 *
 */
void CPreConverter::FullCleanup()
{
    JELOG2(EJavaPreinstaller);

    if (iMidlets)
    {
        iMidlets->ResetAndDestroy();
        delete iMidlets;
        iMidlets = NULL;
    }
}

/**
 * Read all midlets from AppArc.
 * Store midlet Uid, Midlet name, installation drive,
 * group name and icon file path name to iMidlets.
 */
void CPreConverter::ReadMidletsFromAppArcL()
{
    JELOG2(EJavaConverters);

    TInt   retryCounter = 10;
    TUid   appTypeUid;
    HBufC *pIconFilePathName = NULL;
    TApaAppInfo   info;
    RApaLsSession apaSession;
    TApaAppCapabilityBuf cbuf;
    TApaAppCapability    capability;

    TBuf8<512> midletDesc;


    TInt err = apaSession.Connect();
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CPreConverter::ReadMidletsFromAppArcL RApaLsSession Connect error %d", err);
        User::Leave(err);
    }
    CleanupClosePushL(apaSession);

    // Get the process of getting the list of all non native applications
    // (mostly java applications)
    err =
        apaSession.GetFilteredApps(
            TApaAppCapability::ENonNative, TApaAppCapability::ENonNative);

    do
    {
        err = apaSession.GetNextApp(info);
        if (RApaLsSession::EAppListInvalid == err)
        {
            // Application list has not yet been populated,
            // try again after a short delay
            retryCounter--;
            if (retryCounter > 0)
            {
                User::After(KDelayWhenWaitingAppArc);
                continue;
            }
            else
            {
                ELOG(EJavaConverters,
                     "CPreConverter::ReadMidletsFromAppArcL RApaLsSession "
                     "GetNext returned EAppListInvalid for 10 times, error");
                User::Leave(err);
            }
        }
        else if (KErrNone == err)
        {
            // Info contains valid app info. Now check whether it is java app
            err = apaSession.GetAppType(appTypeUid, info.iUid);
            if (KErrNone != err)
            {
                ELOG1(EJavaConverters,
                      "CPreConverter::ReadMidletsFromAppArcL RApaLsSession "
                      "GetAppType returned error %d", err);
                User::Leave(err);
            }
            if (appTypeUid.iUid == KMidletApplicationTypeUid)
            {
                // This is java application. Store info to list.
                CMidletInfo *midletInfo = new(ELeave) CMidletInfo();
                midletInfo->SetMidletUid(info.iUid);
                midletInfo->SetMidletName(info.iCaption);
                midletInfo->SetDrive(ExtractDriveNumberFromPathL(info.iFullName));

                // Get group name
                err = apaSession.GetAppCapability(cbuf, info.iUid);
                if (KErrNone != err)
                {
                    ELOG1(EJavaConverters,
                          "CPreConverter::ReadMidletsFromAppArcL RApaLsSession "
                          "GetAppCapability returned error %d", err);
                    User::Leave(err);
                }
                capability = cbuf();
                midletInfo->SetGroupName(capability.iGroupName);

                // Get icon file path name
                err = apaSession.GetAppIcon(info.iUid, pIconFilePathName);
                if ((KErrNone != err) || (NULL == pIconFilePathName))
                {
                    ELOG1(EJavaConverters,
                          "CPreConverter::ReadMidletsFromAppArcL RApaLsSession "
                          "GetAppIcon returned error %d", err);
                    User::Leave(err);
                }
                midletInfo->SetIconFileName(pIconFilePathName);
                delete pIconFilePathName;
                pIconFilePathName = NULL;

                err = iMidlets->Append(midletInfo);
                if (KErrNone != err)
                {
                    ELOG1(EJavaConverters,
                          "CPreConverter::ReadMidletsFromAppArcL CMidletList "
                          "Append returned error %d", err);
                    User::Leave(err);
                }
                else
                {
                    midletInfo->ToString8(midletDesc);
                    LOG(EJavaConverters, EInfo,
                        "CPreConverter::ReadMidletsFromAppArcL Added this midlet:");
                    midletDesc.ZeroTerminate();
                    LOG(EJavaPreinstaller, EInfo, (const char *)(midletDesc.Ptr()));
                    midletDesc.Zero();
                }
            }
        }

    }
    while (KErrNone == err);

    if (RApaLsSession::ENoMoreAppsInList != err)
    {
        ELOG1(EJavaConverters,
              "CPreConverter::ReadMidletsFromAppArcL RApaLsSession GetNext returned error %d", err);
        User::Leave(err);
    }

    CleanupStack::PopAndDestroy(); // apaSession
}

/**
 * Parse the drive letter from path and return it as TDriveNumber.
 *
 * @param aPathName full path name. Contains always the drive.
 * @return the drive number
 * @exception if the drive cannot parsed from path
 */
TDriveNumber CPreConverter::ExtractDriveNumberFromPathL(TFileName &aPathName)
{
    TDriveNumber drive = EDriveE;
    TUint driveLetter = aPathName[0];

    if ((driveLetter >= 'a') && (driveLetter <= 'z'))
    {
        drive = (TDriveNumber)(driveLetter - 'a');
    }
    else if ((driveLetter >= 'A') && (driveLetter <= 'Z'))
    {
        drive = (TDriveNumber)(driveLetter - 'A');
    }
    else
    {
        ELOG1(EJavaConverters,
              "CPreConverter::ExtractDriveNumberFromPathL path starts "
              "with illegal char, value %d", drive);
        User::Leave(KErrArgument);
    }

    return drive;
}

/**
 * Read the midlet id and suite id for each midlet in iMidlets from
 * SystemAMS DB and store them to iMidlets
 */
void CPreConverter::AddDataFromSystemAmsDbL()
{
    MIDP::DBv2::MDatabase* database = NULL;
    MIDP::DBv2::MMIDletTable* midletTable = NULL;
    RDbNamedDatabase dbNamed;
    RDbs dbs;

    // Connect to db
    TInt err = dbs.Connect();
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CPreConverter::AddDataFromSystemAmsDbL Cannot connect to RDbs, "
              "error %d", err);
        User::Leave(err);
    }

    // Open db
    err = dbs.ShareAuto();
    if (KErrNone != err)
    {
        dbs.Close();
        ELOG1(EJavaConverters,
              "CPreConverter::AddDataFromSystemAmsDbL Cannot share RDbs session, "
              "error %d", err);
        User::Leave(err);
    }
    err = dbNamed.Open(
              iFs, KStaticDbFileName(), KUidSSystemAMSDbPolicy, RDbNamedDatabase::EReadOnly);
    if (KErrNone != err)
    {
        dbs.Close();
        ELOG1(EJavaConverters,
              "CPreConverter::AddDataFromSystemAmsDbL Cannot open named db, "
              "error %d", err);
        User::Leave(err);
    }

    // Get db
    TRAP(err, database = MIDP::DBv2::GetDatabaseL(KDBVersion));
    if (KErrNone != err)
    {
        dbNamed.Close();
        dbs.Close();
        ELOG1(EJavaConverters,
              "CPreConverter::AddDataFromSystemAmsDbL Cannot get database, "
              "error %d", err);
        User::Leave(err);
    }

    // Get midlet table
    TRAP(err, midletTable = database->MIDletTableL());
    if (KErrNone != err)
    {
        delete database;
        dbNamed.Close();
        dbs.Close();
        ELOG1(EJavaConverters,
              "CPreConverter::AddDataFromSystemAmsDbL Cannot get midlet table from db, "
              "error %d", err);
        User::Leave(err);
    }

    // Open midlet table
    TRAP(err, midletTable->OpenL(dbNamed));
    if (KErrNone != err)
    {
        delete database;
        dbNamed.Close();
        dbs.Close();
        ELOG1(EJavaConverters,
              "CPreConverter::AddDataFromSystemAmsDbL Cannot open midlet table in db, "
              "error %d", err);
        User::Leave(err);
    }

    // get iterator to access the data in midlet table
    MIDP::DBv2::MMIDletTableIterator* tableIterator = NULL;
    // No items must be left in cleanup stack when exiting TRAP macro
    TRAP(err,
         tableIterator = midletTable->IteratorLC();
         if (KErrNone == err) CleanupStack::Pop();
        );
    if (KErrNone != err)
    {
        midletTable->Close();
        delete database;
        dbNamed.Close();
        dbs.Close();
        ELOG1(EJavaConverters,
              "CPreConverter::AddDataFromSystemAmsDbL Cannot get iterator for midlet table in db, "
              "error %d", err);
        User::Leave(err);
    }


    TBuf8<512> midletDesc;
    TInt32  count = 0;
    TUint32 msId;
    TUint32 id;
    TUint32 uid;
    TPtrC   name;
    TPtrC   className;
    TPtrC   idString;
    while (tableIterator->HasNext())
    {
        TRAP(err, tableIterator->NextL(msId, id, uid, name, className, idString));
        if (KErrNone != err)
        {
            delete tableIterator;
            midletTable->Close();
            delete database;
            dbNamed.Close();
            dbs.Close();
            ELOG1(EJavaConverters,
                  "CPreConverter::AddDataFromSystemAmsDbL Midlet table iterator NextL failed, "
                  "error %d", err);
            User::Leave(err);
        }
        LOG3(EJavaConverters, EInfo,
             "CPreConverter::AddDataFromSystemAmsDbL Found midlet uid %x, suite id %d, midlet id %d",
             uid, msId, id);

        midletDesc.Append(KMLName);
        midletDesc.Append(name);
        midletDesc.Append(KMLClassName);
        midletDesc.Append(className);
        midletDesc.Append(KMLIdString);
        midletDesc.Append(idString);
        LOG(EJavaPreinstaller, EInfo, (const char *)midletDesc.PtrZ());
        midletDesc.Zero();

        iMidlets->SetIds(name, msId, id);
        count++;
    }

    LOG1(EJavaConverters, EInfo,
         "CPreConverter::AddDataFromSystemAmsDbL midlet table had %d midlets.", count);

    // close and free everything
    delete tableIterator;
    midletTable->Close();
    delete database;
    dbNamed.Close();
    dbs.Close();
}

/**
 * Check all midlets in iMidlets and remove those midlets that do not
 * have midlet id and suite id.
 */
void CPreConverter::RemoveInvalidMidlets()
{
    // If midlet id or suite id is KMaxTUint32, it has not been set ->
    // remove the midlet

    CMidletInfo *midlet = iMidlets->GetFirst();
    TBuf8<512> midletDesc;

    while (NULL != midlet)
    {
        if ((midlet->GetMidletId() == KMaxTUint32) ||
                (midlet->GetSuiteId() == KMaxTUint32))
        {
            midlet->ToString8(midletDesc);
            WLOG(EJavaConverters,
                 "CPreConverter::RemoveInvalidMidlets Not going to convert this midlet:");
            midletDesc.ZeroTerminate();
            WLOG(EJavaPreinstaller, (const char *)(midletDesc.Ptr()));
            midletDesc.Zero();
            iMidlets->Remove(midlet);
        }

        midlet = iMidlets->GetNext();
    }
}

/**
 * Store the uids of the midlets to be converted
 * to file 'uids' in the data cage of javaappconverter.exe
 */
void CPreConverter::StoreUidsL()
{
    TFileName exportUidsPath(KUidsExportDirectory);

    // Create directory if it does not exist
    TInt err= iFs.MkDirAll(exportUidsPath);
    if ((KErrNone != err) && (KErrAlreadyExists != err))
    {
        User::Leave(err);
    }

    // Store all uids to file 'uids' in the directory
    exportUidsPath.Append(KUidsExportDataFileName);

    // Construct write stream so that possibly existing old data file is replaced.
    RFileWriteStream writeStream;
    User::LeaveIfError(writeStream.Replace(iFs, exportUidsPath, EFileWrite));
    CleanupClosePushL(writeStream);

    // Write the number of midlets
    TUint32 nMidlets = iMidlets->Count();
    writeStream.WriteUint32L(nMidlets);

    // Write Uids of the midlets one by one
    CMidletInfo *midlet = iMidlets->GetFirst();

    while (NULL != midlet)
    {
        writeStream.WriteUint32L(midlet->GetMidletUid().iUid);
        midlet = iMidlets->GetNext();
    }

    // Closes writeStream
    CleanupStack::PopAndDestroy();
}

/**
 * Unregisters the applications to be converted from AppArc
 * so that Java Installer can make conversion installation
 * using the same Uids as the applications now have.
 *
 * Leaves with error code if AppArc cannot be connected or if
 * unregistrations cannot be committed.
 */
void CPreConverter::UnregisterOldJavaAppsL()
{
    // connect to AppArc
    RApaLsSession apaSession;

    TInt err = apaSession.Connect();
    if (KErrNone != err)
    {
        // Fatal error, try to connect again. The midlets cannot be converted
        // using the same uids as earlier if the unregistration cannot be done.
        TInt retryCount = 0;
        do
        {
            ELOG1(EJavaConverters,
                  "CPreConverter::UnregisterOldJavaAppsL Cannot connect to "
                  "AppArc server, err %d", err);

            // wait
            User::After(KDelayWhenWaitingAppArc);

            err = apaSession.Connect();
            if (KErrNone == err)
            {
                break;
            }

            retryCount++;
        }
        while (retryCount < 10);

        if (KErrNone != err)
        {
            User::Leave(err);
        }
    }
    CleanupClosePushL(apaSession);

    // Delete any pending (un)registrations (possible if
    // e.g. device rebooted before commit).
    // This call does nothing if there is no pending registrations.
    // Ignore errors.
    (void)apaSession.RollbackNonNativeApplicationsUpdates();

    // Prepare for Java application unregistrations
    TRAP(err, apaSession.PrepareNonNativeApplicationsUpdatesL());
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CPreConverter::UnregisterOldJavaAppsL: "
              "PrepareNonNativeApplicationsUpdatesL leaved with err %d",
              err);
        User::Leave(err);
    }

    // Unregister all apps
    CMidletInfo *midlet = iMidlets->GetFirst();

    while (NULL != midlet)
    {
        TRAP(err, apaSession.DeregisterNonNativeApplicationL(midlet->GetMidletUid()));
        if (KErrNone != err)
        {
            WLOG2(EJavaConverters,
                  "CPreConverter::UnregisterOldJavaAppsL: "
                  "DeregisterNonNativeApplicationL leaved with err %d for uid %d",
                  err, midlet->GetMidletUid().iUid);
            // Ignore error, this particular application cannot be converted.
        }

        midlet = iMidlets->GetNext();
    }

    // Commit unregistrations
    TRAP(err, apaSession.CommitNonNativeApplicationsUpdatesL())
    {
        if (KErrNone != err)
        {
            ELOG1(EJavaConverters,
                  "CPreConverter::UnregisterOldJavaAppsL: "
                  "CommitNonNativeApplicationsUpdatesL leaved with err %d",
                  err);
            User::Leave(err);
        }
    }

    // Closes apaSession
    CleanupStack::PopAndDestroy();
}