javamanager/javalauncher/src.s60/javalauncher.cpp
branchRCL_3
changeset 14 04becd199f91
child 18 9ac0a0a7da70
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javamanager/javalauncher/src.s60/javalauncher.cpp	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,797 @@
+/*
+* Copyright (c) 2005-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:  The executable that enables launching OMJ Java
+*                applications in S60
+*
+*/
+
+#include <apacmdln.h>
+#include <bacline.h>
+#include <e32cmn.h>
+#include <s32mem.h>
+#include <unistd.h>
+
+#include "commsmessage.h"
+#include "commsclientendpoint.h"
+#include "exceptionbase.h"
+#include "javainifileutils.h"
+#include "javaoslayer.h"
+#include "javastorage.h"
+#include "javastorageentry.h"
+#include "javastoragenames.h"
+#include "javasymbianoslayer.h"
+#include "javauids.h"
+#include "logger.h"
+#include "rtcmessages.h"
+#include "runtimeexception.h"
+
+
+using namespace java::captain;
+using namespace java::storage;
+using namespace java::util;
+
+
+_LIT(KSemiColon, ";");
+
+const TInt KExtraLenForLoggingAndPrompting = 22;
+const TInt KArgumentValueMaxLen = 1568;  // Support worst case % encoded args of 512 chars
+
+// The Uid of the Web Browser process
+#define KBrowserUid 0x10008D39
+
+
+/**
+ * Return the Uid of the application aMidletName in package aPackageId.
+ * If aMidletName is empty, return the Uid of the first application in
+ * package aPackageId.
+ *
+ * @param aJs           JavaStorage connection, must be open
+ * @param aPackageId    the id of the package
+ * @param aMidletName   the name of the desired application in the package, can be empty
+ * @param aUid          the Uid the found application is returned in this param
+ * @return  KErrNone if Uid was found, otherwise one of the Symbian error codes
+ * @throws  JavaStorageException if accessing Java Storage fails
+ */
+static TInt getOneApplicationFromPackage(
+    JavaStorage& aJs,
+    const std::wstring& aPackageId,
+    const std::wstring& aMidletName,
+    TInt32 &aUid)
+{
+    JavaStorageEntry attribute;
+    JavaStorageApplicationEntry_t findPattern;
+    JavaStorageApplicationList_t  foundEntries;
+
+    // Get ID from APPLICATION_TABLE based on PACKAGE_ID and NAME
+    attribute.setEntry(PACKAGE_ID, aPackageId);
+    findPattern.insert(attribute);
+    if (aMidletName.length() > 0)
+    {
+        attribute.setEntry(NAME, aMidletName);
+        findPattern.insert(attribute);
+    }
+    attribute.setEntry(ID, L"");
+    findPattern.insert(attribute);
+
+    aJs.search(APPLICATION_TABLE, findPattern, foundEntries);
+
+    if (foundEntries.size() < 1)
+    {
+        return KErrNotFound;
+    }
+
+    std::wstring applicationId = foundEntries.front().begin()->entryValue();
+    TUid tmp;
+    TInt err = uidToTUid(applicationId, tmp);
+    if (KErrNone == err)
+    {
+        aUid = tmp.iUid;
+    }
+
+    return err;
+}
+
+
+/**
+ * Decode %XX encoded UTF-8 characters embedded to UCS-2
+ *
+ * @param aCmdLineBuf  The command line to be decoded.
+ */
+static void decodeCommandLineL(TPtr &aCmdLineBuf)
+{
+    _LIT(KPercentage, "%");
+    TInt ind = aCmdLineBuf.Find(KPercentage);
+    if (KErrNotFound == ind)
+    {
+        // nothing to decode
+        return;
+    }
+
+    LOG(EJavaCaptain, EInfo,
+        "JavaLauncher:decodeCommandLineL convert percent encoded UTF-8 sequences to UTF-16 ");
+
+    try
+    {
+        std::wstring cmdLine = desToWstring(aCmdLineBuf);
+        std::wstring res = JavaCommonUtils::percentDecode(cmdLine);
+
+        LOG1WSTR(EJavaCaptain, EInfo,
+                 "JavaLauncher:decodeCommandLineL: decoded command line is %s", res.c_str());
+
+        aCmdLineBuf = (const TUint16 *)res.c_str();
+    }
+    catch (std::exception& e)
+    {
+        ELOG1(EJavaCaptain,
+              "JavaLauncher:decodeCommandLineL: cannot decode command line: %s", e.what());
+        User::Leave(KErrGeneral);
+    }
+}
+
+
+/**
+ * Return the value of the argument specified by aArgName
+ *
+ * @param aMidletCmdLine command line to be parsed
+ * @param aArgName the name of the argument
+ * @return the value of the argument or empty wstring if no such argument
+ */
+static std::wstring getArgValue(const TPtrC &aMidletCmdLine, const TDesC &aArgName)
+{
+    TBuf<KArgumentValueMaxLen> valueBuf;
+    TInt argPos = aMidletCmdLine.FindF(aArgName);
+    if (argPos >= 0)
+    {
+        TInt semicolonPos = aMidletCmdLine.Mid(argPos).Find(KSemiColon);
+        if (KErrNotFound == semicolonPos)
+        {
+            semicolonPos = aMidletCmdLine.Mid(argPos).Length();
+        }
+        TInt argLen = semicolonPos - aArgName.Length();
+        if (argLen >= KArgumentValueMaxLen)
+        {
+            // Protect from buffer overflow.
+            WLOG2(EJavaCaptain,
+                  "javalauncher: argument value len too long (%d), cutting it to %d",
+                  argLen, (KArgumentValueMaxLen - 1));
+            argLen = KArgumentValueMaxLen - 1;
+        }
+
+        valueBuf = aMidletCmdLine.Mid(argPos + aArgName.Length(),  argLen);
+    }
+    valueBuf.Append('\0');
+
+    std::wstring value = (wchar_t *)&(valueBuf[0]);
+    return value;
+}
+
+
+/**
+ * Parse the names of the MIDlet suite and MIDlet vendor from aMidletCmdLine
+ * parameter and use them to find the MIDlet suite from Java Storage.
+ * Then return Uid of the named MIDlet or if 'midlet-n' argument is not given
+ * in command line, the Uid of the first MIDlet in the suite.
+ * Return the uid of the MIDlet in aUid.
+ *
+ * @param aMidletCmdLine the command line that contains at least midlet-name
+ *  and midlet-vendor arguments
+ * @param aUid if the MIDlet is found from Java Storage, contains the Uid after return.
+ * @return KErrNone if MIDlet was found. KErrPathNotFound if MIDlet is not found.
+ *  Standard Symbian error codes in other cases.
+ */
+static TInt getUidByNames(const TPtrC &aMidletCmdLine, TInt32 &aUid)
+{
+    _LIT(KMidletNameArg, "midlet-name=");
+    _LIT(KMidletVendorArg, "midlet-vendor=");
+    _LIT(KMidletNArg, "midlet-n=");
+
+    TInt err = aMidletCmdLine.FindF(KMidletNameArg);
+    if (KErrNotFound == err)
+    {
+        return KErrArgument;
+    }
+    err = aMidletCmdLine.FindF(KMidletVendorArg);
+    if (KErrNotFound == err)
+    {
+        return KErrArgument;
+    }
+
+    std::wstring suiteName = getArgValue(aMidletCmdLine, KMidletNameArg);
+    std::wstring vendorName = getArgValue(aMidletCmdLine, KMidletVendorArg);
+    std::wstring midletName = getArgValue(aMidletCmdLine, KMidletNArg);
+
+    if (suiteName.empty() || vendorName.empty())
+    {
+        return KErrArgument;
+    }
+
+    // Find application uid based on names from Java Storage
+    JavaStorage *js = JavaStorage::createInstance();
+
+    try
+    {
+        js->open(JAVA_DATABASE_NAME);
+
+        JavaStorageEntry attribute;
+        JavaStorageApplicationEntry_t findPattern;
+        JavaStorageApplicationList_t  foundEntries;
+
+        // Search for package ID by PACKAGE_NAME and VENDOR from APPLICATION_PACKAGE_TABLE
+        attribute.setEntry(PACKAGE_NAME, suiteName);
+        findPattern.insert(attribute);
+        attribute.setEntry(VENDOR, vendorName);
+        findPattern.insert(attribute);
+        attribute.setEntry(ID, L"");
+        findPattern.insert(attribute);
+
+        js->search(APPLICATION_PACKAGE_TABLE , findPattern, foundEntries);
+
+        // Is anything found
+        if (foundEntries.size() > 0)
+        {
+            // The application package has been found, get the ID of the package
+            std::wstring value = foundEntries.front().begin()->entryValue();
+            LOG1WSTR(EJavaCaptain, EInfo,
+                     "JavaLauncher: getUidByNamesL: Found suite uid by name. Uid is %s",
+                     value.c_str());
+
+            // Now find the Uid of the first or specified application in the package
+            err = getOneApplicationFromPackage(*js, value, midletName, aUid);
+        }
+        else
+        {
+            err = KErrPathNotFound;
+        }
+    }
+    catch (JavaStorageException& e)
+    {
+        ELOG1(EJavaCaptain, "Java Storage exception %s", e.what());
+        err = KErrGeneral;
+    }
+
+    try
+    {
+        js->close();
+    }
+    catch (JavaStorageException& e2)
+    {
+        WLOG1(EJavaCaptain, "Java Storage exception when closing storage %s", e2.what());
+    }
+
+    delete js;
+
+    return err;
+}
+
+
+/**
+ * Check whether the MIDlet has been installed to device.
+ *
+ * @param aUid the Uid of the MIDlet to be checked
+ * @return KErrNone if the MIDlet has been installed,
+ *         KErrNotFound if the MIDlet has not been installed,
+ *         general Symbian error codes in case of error
+ */
+static TInt verifyAppExists(TInt32 aUid)
+{
+    TInt err = KErrNone;
+
+    if (aUid == 0)
+    {
+        return KErrNotFound;
+    }
+
+    // Find application uid based on names from Java Storage
+    JavaStorage *js = JavaStorage::createInstance();
+
+    try
+    {
+        js->open(JAVA_DATABASE_NAME);
+
+        JavaStorageEntry attribute;
+        JavaStorageApplicationEntry_t findPattern;
+        JavaStorageApplicationList_t  foundEntries;
+
+        // Search for application NAME by ID from APPLICATION_TABLE
+        Uid uid;
+        TUid tuid;
+        tuid.iUid = aUid;
+        TUidToUid(tuid, uid);
+        attribute.setEntry(ID, uid.toString());
+        findPattern.insert(attribute);
+        attribute.setEntry(NAME, L"");
+        findPattern.insert(attribute);
+
+        js->search(APPLICATION_TABLE , findPattern, foundEntries);
+
+        // Is anything found
+        if (foundEntries.size() > 0)
+        {
+            err = KErrNone;
+        }
+        else
+        {
+            err = KErrNotFound;
+        }
+    }
+    catch (JavaStorageException& e)
+    {
+        ELOG1(EJavaCaptain, "Java Storage exception %s", e.what());
+        err = KErrGeneral;
+    }
+
+    try
+    {
+        js->close();
+    }
+    catch (JavaStorageException& e2)
+    {
+        WLOG1(EJavaCaptain, "Java Storage exception when closing storage %s", e2.what());
+    }
+
+    delete js;
+
+    return err;
+}
+
+
+/**
+ * Either parse the Uid from the value of 'midlet-uid' parameter in
+ * command line given in aMidletCmdLine or find the midlet
+ * based on the 'midlet-name' and 'midlet-vendor' from
+ * Java Storage / AppArc and return the Uid of the midlet.
+ *
+ * @param aMidletCmdLine  command line to be parsed, the format is
+ *  [midlet-name=XXX;midlet-vendor=XXX;|midlet-uid=YYY;]midlet-args=XXX
+ * @param aUid will contain the Uid parsed from command line
+ * @return KErrNone if the command line specified Uid
+ */
+static TInt getUidFromCommandLine(const TPtrC &aMidletCmdLine, TInt32 &aUid)
+{
+    _LIT(KMidletUidArg, "midlet-uid=");
+    _LIT(KHexValueStart, "0x");
+    TInt err(KErrNone);
+    TInt argPos = aMidletCmdLine.FindF(KMidletUidArg);
+    if (KErrNotFound != argPos)
+    {
+        TPtrC uidToParse = aMidletCmdLine.Mid(argPos + KMidletUidArg.iTypeLength);
+        TLex parseUid(uidToParse);
+        if (uidToParse.FindF(KHexValueStart) == 0)
+        {
+            parseUid.Inc(2); // skip hex prefix
+            TUint32 tmpValue;
+            err = parseUid.Val(tmpValue, EHex);
+            aUid = tmpValue;
+        }
+        else
+        {
+            err = parseUid.Val(aUid);
+        }
+
+        if (KErrNone != err)
+        {
+            ELOG1(EJavaCaptain,
+                  "javalauncher failed parsing app Uid from cmdline midlet-uid param. Error %d",
+                  err);
+        }
+
+        err = verifyAppExists(aUid);
+    }
+    else
+    {
+        err = getUidByNames(aMidletCmdLine, aUid);
+        if (KErrNone != err)
+        {
+            if (KErrArgument == err)
+            {
+                // In this case the Uid is not in opaque data and there are
+                // no midlet-uid or midlet-name + midlet-vendor parameters in the command line
+                ELOG(EJavaCaptain,
+                     "javalauncher cannot launch app because there is no info to determine Uid.");
+            }
+            else
+            {
+                ELOG1(EJavaCaptain,
+                      "javalauncher failed getting app Uid based on cmdline midlet-name and "
+                      "midlet-vendor params. Error %d",
+                      err);
+            }
+        }
+    }
+
+    return err;
+}
+
+
+/**
+ * Parse CApaCommandLine and optional normal process command line.
+ * Determine the uid of the Java application to be started
+ *
+ * @param aCmdLine returns value of midlet-args
+ * @param aUid returns Uid of the Java application to be started
+ * @param aBackGroundLaunch returns info whether back ground launch is needed
+ * @return KErrNone or error code
+ */
+TInt getCmdLineAndUidL(HBufC **aCmdLine, TInt *aUid, TBool *aBackGroundLaunch)
+{
+    CApaCommandLine* commandLine;
+
+    // CApaCommandLine command line is used when AppArc is launching Java application.
+    // Uid is in opaque data and possible document name is in document name field.
+    TInt err = CApaCommandLine::GetCommandLineFromProcessEnvironment(commandLine);
+    if (KErrNone != err)
+    {
+        return err;
+    }
+    CleanupStack::PushL(commandLine);
+
+    // The following feature is not supported yet.
+    // It needs changes also to Java Installer and to
+    // Symbian Settings UI
+    TInt maxExtraSpaceForDocumentArg = 0;
+#ifdef RD_JAVA_SUPPORT_JAVA_APPS_AS_MIME_TYPE_HANDLERS
+    _LIT(KDocumentOnlyCmdLine, "midlet-args=document=");
+    TFullName documentName;
+    documentName = commandLine->DocumentName();
+    if (documentName.Length() > 0)
+    {
+        maxExtraSpaceForDocumentArg =
+            documentName.Length() + KDocumentOnlyCmdLine.iTypeLength +
+            KSemiColon.iTypeLength;
+
+        LOG1WSTR(EJavaCaptain, EInfo,
+                 "JavaLauncher: document to handle is %s",
+                 (wchar_t *)(documentName.PtrZ()));
+    }
+#endif
+
+    // User::CommandLine command line is used when Java application is launched
+    // from native application or from javaapp: or localapp:/jam/launch/ scheme
+    // handler plugin.
+    HBufC *pBufCmdLine =
+        HBufC::NewLC(User::CommandLineLength() +
+                     maxExtraSpaceForDocumentArg +
+                     KExtraLenForLoggingAndPrompting);
+    TPtr cmdLineBuf = pBufCmdLine->Des();
+    User::CommandLine(cmdLineBuf);
+
+    if (pBufCmdLine->Length() > 0)
+    {
+        LOG1WSTR(EJavaCaptain, EInfo,
+                 "JavaLauncher: command line of this process is %s",
+                 (wchar_t *)(cmdLineBuf.PtrZ()));
+
+        // If the commandline contains hex encoded UTF-8 characters %XX decode them to UCS-2 (UTF-16)
+        // For example "%C3%80%C3%80nes%C3%80.txt"
+        decodeCommandLineL(cmdLineBuf);
+    }
+
+    TPtrC8 data = commandLine->OpaqueData();
+    TInt32 uid = 0;  // application uid
+    // New OMJ Java applications have 4 bytes of opaque data.
+    TBool uidInOpaqueData = (data.Length() == 4);
+    TBool backGroundLaunch =
+        (EApaCommandBackground == commandLine->Command()) ||
+        (EApaCommandBackgroundAndWithoutViews == commandLine->Command());
+
+    if (uidInOpaqueData)
+    {
+        RDesReadStream stream(data);
+        // OMJ java application, read the application uid
+        TRAP(err, uid = stream.ReadInt32L());
+        stream.Close();
+        if (KErrNone != err)
+        {
+            ELOG1(EJavaCaptain,
+                  "javalauncher failed reading app Uid from cmdline opaque data. Error %d",
+                  err);
+
+            CleanupStack::PopAndDestroy(pBufCmdLine);
+            CleanupStack::PopAndDestroy(commandLine);
+            return err;
+        }
+    }
+    else
+    {
+        // Can the midlet uid be determined using info in the commandline?
+        err = getUidFromCommandLine(cmdLineBuf, uid);
+        if (KErrNone != err)
+        {
+            CleanupStack::PopAndDestroy(pBufCmdLine);
+            CleanupStack::PopAndDestroy(commandLine);
+            return err;
+        }
+    }
+
+#ifdef RD_JAVA_SUPPORT_JAVA_APPS_AS_MIME_TYPE_HANDLERS
+    // Add possible document name to Java application command line.
+
+    // javalauncher.exe can be started with CApaCommandLine coming from AppArc
+    // that may specify document name but no other arguments for Java application.
+    // In this case the current command line is empty and we must create a command
+    // line that contains the document name.
+    if (pBufCmdLine->Length() == 0)
+    {
+        if (documentName.Length() > 0)
+        {
+            cmdLineBuf.Append(KDocumentOnlyCmdLine);
+            cmdLineBuf.Append(documentName);
+            cmdLineBuf.Append(KSemiColon);
+        }
+    }
+#endif
+
+    if (cmdLineBuf.Length() > 0)
+    {
+        LOG1WSTR(EJavaCaptain, EInfo,
+                 "JavaLauncher: full java application cmd line is : %s",
+                 (wchar_t *)(cmdLineBuf.PtrZ()));
+    }
+
+    // Uid has already been determined, the whole command line is not needed
+    // anymore, only the arguments for the Java application
+    _LIT(KMidletArgs, "midlet-args=");
+    TInt  argsPos = cmdLineBuf.FindF(KMidletArgs);
+    TBool cmdLineIsNotEmpty = EFalse;
+    if (argsPos >= 0)
+    {
+        // Pass everything that follows "midlet-args="
+        cmdLineBuf.Delete(0, argsPos + KMidletArgs.iTypeLength);
+        cmdLineIsNotEmpty = ETrue;
+    }
+    else
+    {
+        // No arguments for the Java application
+        cmdLineBuf.Zero();
+    }
+
+    // If Browser starts Java application with
+    // command line parameters, the user must be asked for a confirmation
+    // before the application is started.
+    // This prevents automatic starting of Java applications from a web
+    // page using 'javaapp:' url and java script.
+    TUid parentUid = User::CreatorIdentity();
+    LOG1(EJavaCaptain, EInfo, "JavaLauncher: Parent process uid is %x", parentUid.iUid);
+    if (parentUid.iUid == KBrowserUid)
+    {
+        // Pass information that the application start up must be prompted
+        // in the java application command line
+        if (cmdLineIsNotEmpty)
+        {
+            cmdLineBuf.Append(KSemiColon);
+        }
+        _LIT(KPromptStart, "PromptAppStartup");
+        cmdLineBuf.Append(KPromptStart);
+    }
+
+    // Return command line, uid and info whether this is back ground launch
+    *aBackGroundLaunch = backGroundLaunch;
+    *aUid = uid;
+    CleanupStack::Pop(pBufCmdLine);
+    *aCmdLine = pBufCmdLine;
+    CleanupStack::PopAndDestroy(commandLine);
+
+    return KErrNone;
+}
+
+
+/**
+ *  Start Java Captain process
+ */
+TInt startJavaCaptain()
+{
+    // Start 'systemams.exe' in S60 5.0 (it will start javacaptain) and
+    // 'javacaptain.exe' in 9.2 and later
+
+#ifdef RD_JAVA_S60_RELEASE_5_0_IAD
+    _LIT(KJavaCaptainExe, "systemams.exe");
+#else
+    _LIT(KJavaCaptainExe, "javacaptain.exe");
+#endif
+    _LIT(KJavaCaptainArg, "");
+    RProcess proc;
+    int err = proc.Create(KJavaCaptainExe, KJavaCaptainArg);
+    if (err == KErrNone)
+    {
+        proc.Resume();
+#ifdef RD_JAVA_S60_RELEASE_5_0_IAD
+        LOG(EJavaCaptain, EInfo, "javalauncher: startJavaCaptain systemams.exe was started ok");
+#else
+        LOG(EJavaCaptain, EInfo, "javalauncher: startJavaCaptain javacaptain.exe was started ok");
+#endif
+
+        // Wait 3 seconds so that Java Captain has time to start
+        User::After(3000000);
+    }
+    else
+    {
+#ifdef RD_JAVA_S60_RELEASE_5_0_IAD
+        ELOG1(EJavaCaptain, "javalauncher: startJavaCaptain start systemams.exe failed: %d", err);
+#else
+        ELOG1(EJavaCaptain, "javalauncher: startJavaCaptain start javacaptain.exe failed: %d", err);
+#endif
+    }
+    proc.Close();
+
+    return err;
+}
+
+
+/**
+ * Determine the uid of the Java application to be started and
+ * ask Java Captain to launch the application.
+ * Pass parsed arguments for the application in the Comms message
+ * that is sent to Java Captain to start the applciation.
+ *
+ * @return KErrNone or error code
+ */
+TInt handleLaunchL(void)
+{
+    HBufC *pBufCmdLine = NULL;
+    TInt   uid;
+    TBool  backGroundLaunch = EFalse;
+    TInt err = getCmdLineAndUidL(&pBufCmdLine, &uid, &backGroundLaunch);
+    if (KErrNone != err)
+    {
+        return err;
+    }
+    TPtr cmdLineBuf = pBufCmdLine->Des();
+
+    LOG1(
+        EJavaCaptain,
+        EInfo,
+        "javalauncher launching app uid %d", uid);
+
+    try
+    {
+        JavaOsLayer::startUpTrace("JavaLauncher: starting Comms", -1, -1);
+        CommsMessage message;
+        message.setModuleId(PLUGIN_ID_RTC_C);
+        message.setReceiver(IPC_ADDRESS_JAVA_CAPTAIN_C);
+        message.setSender(IPC_ADDRESS_JAVA_CAPTAIN_C);
+        TUid tuid = TUid::Uid(uid);
+        Uid uuid;
+        std::wstring applicationArguments(desToWstring(cmdLineBuf));
+
+        setLaunchApplicationReqParams(message, TUidToUid(tuid, uuid),
+                                      (backGroundLaunch ? RTC_LAUNCH_TYPE_BACKGROUND_C : RTC_LAUNCH_TYPE_NORMAL_C),
+                                      RTC_LAUNCH_OPTIONS_NONE_C,
+                                      RTC_LAUNCH_RUNTIME_MIDP_C,
+                                      applicationArguments);
+
+        CommsClientEndpoint comms;
+        err = comms.connect(IPC_ADDRESS_JAVA_CAPTAIN_C);
+        if (KErrNotFound == err)
+        {
+            ELOG(EJavaCaptain,
+                 "javalauncher: Java Captain was not running. Trying to restart it.");
+
+            // Java Captain is not running, try to start it
+            err = startJavaCaptain();
+            if (KErrNone == err)
+            {
+                err = comms.connect(IPC_ADDRESS_JAVA_CAPTAIN_C);
+            }
+        }
+
+        if (KErrNone == err)
+        {
+            err = comms.send(message);
+        }
+
+        if (KErrNone != err)
+        {
+            ELOG1(EJavaCaptain,
+                  "javalauncher: OMJ app launch: Comms connect/send failed. Error %d ",
+                  err);
+        }
+
+        // Ignore possible errors in disconnect
+        (void)comms.disconnect();
+    }
+    catch (ExceptionBase& e)
+    {
+        ELOG1(EJavaCaptain,
+              "javalauncher: OMJ app launch: ExceptionBase caught: %s ",
+              e.toString().c_str());
+    }
+    catch (std::exception& e)
+    {
+        ELOG1(EJavaCaptain,
+              "javalauncher: OMJ app launch: Exception %s caught", e.what());
+    }
+
+    return err;
+}
+
+
+/**
+ * Main function of executable javalauncher.exe.
+ * Symbian AppArc starts this executable when AppArc APIs are used to
+ * start a Java application that has been registered to AppArc.
+ * (Because this application has been registered to AppArc as the starter
+ * application for all OMJ Java applications.)
+ *
+ * Reads the opaque data of the CApaCommandLine command line and
+ * if the opaque data contains valid OMJ Java application Uid
+ * sends a COMMS message to Java Captain asking that Captain starts
+ * the application specified by the Uid.
+ *
+ * The document field of the CApaCommandline command line may contain the full path
+ * name of the document that the java application should handle. It will be passed
+ * to the Java application as an argument.
+ *
+ * This executable can be started also directly from other native processes
+ * to start a Java application with arguments specified in normal process
+ * command line.
+ *
+ * The command line format is
+ * [midlet-name=XXX;midlet-vendor=XXX;[midlet-n=XXX;]|midlet-uid=YYY;]midlet-args=XXX
+ * for example
+ * midlet-name=Chess;midlet-vendor=Nokia;midlet-args=startMode=playChessDemo;sound=off;
+ * 'midlet-args' specifies the arguments passed to Java application.
+ * 'midlet-uid' or 'midlet-name'+'midlet-vendor' specify the Java application to be started
+ *
+ * Sample code for starting MIDlet from native code
+ * @code
+    RProcess rProcess;
+    TInt err = rProcess.Create(_L("javalauncher.exe"),
+        _L("midlet-uid=0x10137c4d;midlet-args=startMode=startFromCmdLine;sound=ON;landscapeMode=true;"));
+    if (KErrNone == err)
+    {
+        TRequestStatus status;
+        rProcess.Logon(status);
+        rProcess.Resume();
+
+        // now wait until javalauncher exits
+        User::WaitForRequest(status);
+        if (status.Int() != KErrNone)
+        {
+            // Launching midlet failed
+            ...
+        }
+    }
+    else
+    {
+        // Cannot start javalauncher.exe
+        ...
+    }
+    rProcess.Close();
+ * @endcode
+ *
+ */
+TInt E32Main(void)
+{
+    JavaOsLayer::startUpTrace("JavaLauncher: Start", -1, -1);
+
+    CTrapCleanup *pCleanupStack = CTrapCleanup::New();
+    if (NULL == pCleanupStack)
+    {
+        ELOG(EJavaCaptain, "Cannot create CleanupStack in javalauncher main()");
+        return KErrNoMemory;
+    }
+
+    TInt ret = KErrNone;
+    TRAPD(err, ret = handleLaunchL());
+    if (KErrNone != err)
+    {
+        ELOG1(EJavaCaptain, "javalauncher.exe: handleLaunchL leaved with err %d", err);
+        delete pCleanupStack;
+        return err;
+    }
+
+    delete pCleanupStack;
+    return ret;
+}
+