javatools/javaappbackconverter/src.s60/backconverter.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 30 Apr 2010 10:40:48 +0300
branchRCL_3
changeset 20 f9bb0fca356a
parent 19 04becd199f91
permissions -rw-r--r--
adding j9 directory to week17 release

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Java platform 2.0 javaappbackconverter process.
*              Reregisters the old S60 MIDlets back to AppArc
*
*/


#include <e32base.h>
#include <apgicnfl.h>
#include <s32mem.h>
#include <e32property.h>

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


const TInt KDelayWhenWaitingAppArc = 500000;

// This is in the private data cage of javaappbackconverter
_LIT(KMidletImportDirectory, "C:\\private\\20022D90\\data\\");

// Symbian file path separator
_LIT(KPathSeperator, "\\");

// Postfix for the fake application name generated only to make AppArc happy
_LIT(KAppPostfix, ".fakeapp");

// The application type Uid for MIDlets in S60
const TUid KUidMidletApplicationType = { 0x10210E26 };

/**
 * 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.
 *
 */
CBackConverter* CBackConverter::NewLC(RFs& aFs)
{
    CBackConverter* self = new(ELeave) CBackConverter(aFs);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
}

/**
 * To do 1st phase construction for this object.
 *
 * Adds this active object to the scheduler.
 *
 * @param param aFs - A reference to the file server.
 * @return Reference to the object of this class.
 */
CBackConverter::CBackConverter(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 CBackConverter::ConstructL()
{
    JELOG2(EJavaConverters);

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

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

/**
 * To start back conversion
 */
void CBackConverter::Start()
{
    JELOG2(EJavaConverters);
    iState = EInitialize;
    CompleteRequest();
}

/**
 * To stop whole back conversion.
 * Stops the active scheduler.
 */
void CBackConverter::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 CBackConverter::CompleteRequest()
{
    JELOG2(EJavaConverters);

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

/**
 * To run this active object.
 *
 * The state logic is:
 *
 * EInitialize:
 *   - if midlet export data file does not exist, exit
 *
 * EReadConversionData:
 *   - read data from data file to iMidlets
 *
 * EReregisterMidlets:
 *   - register each midlet in iMidlets to AppArc
 *
 * EExit:
 *   - free resources and exit
 *
 */
void CBackConverter::RunL()
{
    JELOG2(EJavaConverters);

    switch (iState)
    {

    case EInitialize:
    {
        LOG(EJavaConverters, EInfo,
            "CBackConverter::RunL EInitialize");

        TFileName dataFile(KMidletImportDirectory);
        dataFile.Append(KMidletExportDataFileName);
        TUint attributes;
        TInt  err = iFs.Att(dataFile, attributes);
        if (KErrNone != err)
        {
            WLOG1(EJavaConverters,
                  "CBackConverter::RunL EInitialize "
                  "Data file does not exist (%d). Cannot do back conversion.", err);
            LOG1WSTR(EJavaConverters, EInfo,
                     "Data file name is %s", (wchar_t *)(dataFile.PtrZ()));
            iState = EExit;
        }
        else
        {
            iState = EReadConversionData;
        }
        CompleteRequest();
    }
    break;

    case EReadConversionData:
    {
        LOG(EJavaConverters, EInfo,
            "CBackConverter::RunL EReadConversionData");

        // Read all midlet info from data file to iMidlets
        TFileName importDirectory(KMidletImportDirectory);
        iMidlets->ImportListL(iFs, importDirectory);

        iState = EUnregisterOldMidletData;
        CompleteRequest();
    }
    break;

    case EUnregisterOldMidletData:
    {
        LOG(EJavaConverters, EInfo,
            "CBackConverter::RunL EUnregisterOldMidletData");

        // If executing backconverter after user cancel,
        // AppArc may contain Java 2.0 specific data
        // for converted midlets. Remove it or reregistration
        // will not update the data.
        UnregisterOldMidletData();

        iState = EReregisterMidlets;
        CompleteRequest();
    }
    break;

    case EReregisterMidlets:
    {
        LOG(EJavaConverters, EInfo,
            "CBackConverter::RunL EReregisterMidlets");

        // Reregister old midlet info back to AppArc
        RegisterMidletsL();

        // If this line is executed, registering old midlets
        // back to AppArc succeeded. So the data file can be
        // removed.
        RemoveDataFile();

        iState = EExit;
        CompleteRequest();
    }
    break;

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

        FullCleanup();

        // The whole javaappbackconverter 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 CBackConverter::RunError(TInt aError)
{
    ELOG2(EJavaConverters,
          "CBackConverter::RunError(%d) from state %d", aError, iState);

    Cancel();

    iState = EExit;
    CompleteRequest();

    return KErrNone;
}

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

}

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

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

/**
 * Register all midlets in iMidlets to AppArc.
 */
void CBackConverter::RegisterMidletsL()
{
    RApaLsSession apaSession;

    // open AppArc session
    TInt err = apaSession.Connect();
    if (KErrNone != err)
    {
        // Cannot connect to AppArc server
        ELOG(EJavaConverters,
             "CBackConverter::RegisterMidletsL: Cannot connect to AppArc server");
        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 registrations / unregistrations
    TRAP(err, apaSession.PrepareNonNativeApplicationsUpdatesL());
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CBackConverter::RegisterMidletsL: "
              "PrepareNonNativeApplicationsUpdatesL leaved with err %d",
              err);
        User::Leave(err);
    }

    // register midlets one by one
    CMidletInfo *midlet = iMidlets->GetFirst();
    TBuf8<512>   midletDesc;

    while (NULL != midlet)
    {
        midlet->ToString8(midletDesc);
        WLOG(EJavaConverters,
             "CBackConverter::RegisterMidletsL Going to reregister this midlet:");
        midletDesc.ZeroTerminate();
        WLOG(EJavaPreinstaller, (const char *)(midletDesc.Ptr()));
        midletDesc.Zero();

        RegisterOneMidletL(&apaSession, midlet);
        midlet = iMidlets->GetNext();
    }

    // commit registrations
    TRAP(err, apaSession.CommitNonNativeApplicationsUpdatesL());
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CBackConverter::RegisterMidletsL: "
              "CommitNonNativeApplicationsUpdatesL leaved with err %d",
              err);
        User::Leave(err);
    }

    CleanupStack::PopAndDestroy(); // apaSession
}

/**
 * Register one midlet to AppArc
 *
 * @param aSession AppArc session
 * @param aMidlet  the midlet to be registered
 * @exception If registering application fails
 */
void CBackConverter::RegisterOneMidletL(RApaLsSession *aSession, CMidletInfo *aMidlet)
{
    JELOG2(EJavaPreinstaller);

    RFile appArcIcon;
    TInt  numberOfIcons = 1;

    // open the icon in the shared mode required by AppArc
    TInt err = appArcIcon.Open(
                   iFs,
                   aMidlet->GetIconFileName(),
                   (EFileShareReadersOrWriters | EFileWrite));
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CBackConverter::RegisterOneMidletL: Cannot open icon, err code is %d ",
              err);
        User::Leave(err);
    }

    // Now icon is open
    CleanupClosePushL(appArcIcon);

    // Generate the executable name name using the same
    // algorithm as used earlier in S60 platform
    // in case some external S60 application
    // needs to parse the Uid from the executable name.
    TFileName appName;
    appName.Copy(KPathSeperator);
    appName.AppendNum(aMidlet->GetMidletUid().iUid);
    appName.Append(KAppPostfix);

    // Create writer needed for actual registration
    CApaRegistrationResourceFileWriter *writer = NULL;
    writer =
        CApaRegistrationResourceFileWriter::NewL(
            aMidlet->GetMidletUid(),
            appName,
            TApaAppCapability::ENonNative);
    CleanupStack::PushL(writer);

    writer->SetGroupNameL(aMidlet->GetGroupName());

    // Write MIDlet suite id and MIDlet id to opaque data  (needed by S60 MIDlet launcher)
    TBuf8<8>         opaqueData;     // opaque data will contain two signed 32-bit int
    RDesWriteStream  writeStream(opaqueData);
    writeStream.WriteInt32L(aMidlet->GetSuiteId());
    writeStream.WriteInt32L(aMidlet->GetMidletId());
    writeStream.CommitL();
    writer->SetOpaqueDataL(opaqueData);

    CApaLocalisableResourceFileWriter*  lWriter = NULL;
    lWriter =
        CApaLocalisableResourceFileWriter::NewL(
            KNullDesC,  // short caption
            aMidlet->GetMidletName(),  // caption
            numberOfIcons,
            KNullDesC);
    CleanupStack::PushL(lWriter);

    TDriveUnit targetDrive(aMidlet->GetDrive());

    // Register application to AppArc
    aSession->RegisterNonNativeApplicationL(
        KUidMidletApplicationType,
        targetDrive,
        *writer,
        lWriter,
        &appArcIcon);

    CleanupStack::PopAndDestroy(lWriter);
    CleanupStack::PopAndDestroy(writer);
    // close icon file handle
    CleanupStack::PopAndDestroy();  // appArcIcon
}

/**
 * Unregisters the applications to be back converted from AppArc
 * so that they can be registered again with the same Uid but with
 * new opaque data (needed for old S60 Java).
 * This function needs to called only if making back conversion after
 * the user has canceled the installation of Java 2.0.
 * If back conversion is done while uninstalling Java 2.0, all Java applications
 * have been uninstalled and there are no Uids to be unregistered.
 *
 * Leaves with error code if AppArc cannot be connected or if
 * unregistrations cannot be committed.
 */
void CBackConverter::UnregisterOldMidletData()
{
    // connect to AppArc
    RApaLsSession apaSession;

    TInt err = apaSession.Connect();
    if (KErrNone != err)
    {
        // Fatal error, try to connect again. The midlets cannot be back converted
        // using the same uids as earlier if the unregistration cannot be done.
        TInt retryCount = 0;
        do
        {
            ELOG1(EJavaConverters,
                  "CBackConverter::UnregisterCurrentJavaAppsL 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)
        {
            return;
        }
    }

    // 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,
              "CBackConverter::UnregisterCurrentJavaAppsL: "
              "PrepareNonNativeApplicationsUpdatesL leaved with err %d",
              err);
        apaSession.Close();
        return;
    }

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

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

        midlet = iMidlets->GetNext();
    }

    // Commit unregistrations
    TRAP(err, apaSession.CommitNonNativeApplicationsUpdatesL())
    {
        if (KErrNone != err)
        {
            ELOG1(EJavaConverters,
                  "CBackConverter::UnregisterCurrentJavaAppsL: "
                  "CommitNonNativeApplicationsUpdatesL leaved with err %d",
                  err);
            apaSession.Close();
            return;
        }
    }

    apaSession.Close();
    return;
}

/**
 * Remove data file that contains info about midlets to be back converted.
 */
void CBackConverter::RemoveDataFile()
{
    TFileName dataFile(KMidletImportDirectory);
    dataFile.Append(KMidletExportDataFileName);

    TInt err = iFs.Delete(dataFile);
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CBackConverter::RemoveDataFile: Cannot delete data file, err %d ",
              err);
    }
}