javatools/javaappbackconverter/src.s60/backconverter.cpp
branchRCL_3
changeset 14 04becd199f91
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javatools/javaappbackconverter/src.s60/backconverter.cpp	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,570 @@
+/*
+* 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);
+    }
+}