diff -r f5050f1da672 -r 04becd199f91 javamanager/javalauncher/src.s60/javalauncher.cpp --- /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 +#include +#include +#include +#include + +#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 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; +} +