javaruntimes/midp/runtimestarter/src/midpruntimestarter.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 14:23:59 +0300
branchRCL_3
changeset 83 26b2b12093af
parent 77 7cee158cb8cd
permissions -rw-r--r--
Revision: v2.2.17 Kit: 201041

/*
* Copyright (c) 2009-2010 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:  This class provides container for message.
*
*/


#include <string>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <unistd.h>

#ifndef __SYMBIAN32__
#include <signal.h>
#endif // __SYMBIAN32__

#include "midpruntimestarter.h"
#include "applicationinfosetter.h"
#include "runtimeexception.h"
#include "runtimestarterutils.h"
#include "midpruntimearguments.h"

#include "dynamiclibloader.h"
#include "javainifileutils.h"
#include "javacommonutils.h"
#include "javaoslayer.h"
#include "logger.h"

#include "commsmessage.h"
#include "rtcmessages.h"

#include "pushcontrollerstarter.h"

#include "javastoragenames.h"

#include "javacoreui.h"
#include "javacoreuiparams.h"

#include "com_nokia_mj_impl_rt_midp_MemoryLogger.h"

using namespace java::runtime;
using namespace java::util;
using namespace java::push;
using namespace java::storage;
using namespace java::captain;
using namespace java::ui;

const wchar_t* const RUNTIME_MAIN_CLASS = L"com.nokia.mj.impl.rt.midp.Main";
const wchar_t* const TRUE_WSTR          = L"true";


MidpRuntimeStarter::MidpRuntimeStarter(): mMidletInfo(new MidletInfo()), // codescanner::nonleavenew
        mRuntimeState(Constructed), mShudownOk(false)
{
    JELOG2(EJavaRuntime);
}

MidpRuntimeStarter::~MidpRuntimeStarter()
{
    JELOG2(EJavaRuntime);
    if (mPushLib.get())
    {
        mPushLib->closeLib();
    }
}

int MidpRuntimeStarter::start(int argc, char *argv[])
{
    JELOG2(EJavaRuntime);

    // Create instance of RuntimeStarterUtils for thread supervisioning.
    std::auto_ptr<RuntimeStarterUtils> starterUtils(new RuntimeStarterUtils()); // codescanner::nonleavenew
    starterUtils->startThreadSupervisor();

    // Parse the args received from Captain.
    parseArgs(argc, argv);

    initComms();

    // Some push plugins needs to get the UID of the MIDlet and the root
    // path. Setting MidpRuntimeStarter as a service provider.
    setApplicationInfoProvider(*this);

    // Check if push command was receieved.
    if (mMidletInfo->mPushStart)
    {
        // In order ot serve ApplicationInfo we need to solve the
        // MIDlet suite UID.
        std::auto_ptr<JavaStorage> storage(JavaStorage::createInstance());
        storage->open();
        getMIDletSuiteUidFromStorage(*storage.get());
        storage->close();
        storage.reset(0);

        handlePushStart();
        // Handle case where during the push listening we receive a close cmd.
        if (mRuntimeState == Closing)
        {
            closePush();
            return 0;
        }
    }
    // Load the core UI
    std::auto_ptr<java::util::DynamicLibLoader> coreUiLoader;
    startCoreUi(coreUiLoader);

    // Create starter for starting the JVM
    std::auto_ptr<JvmStarter>
    jvm(JvmStarter::getJvmStarterInstance(JvmStarter::CLDC,
                                          L"Midp"));

    // Don't know the class path in pre-warm state.
    if (!mMidletInfo->mPreWarmStart)
    {
        jvm->appendClassPath(mMidletInfo->mClassPath);
        jvm->appendApplicationArgument(L"-uid");
        jvm->appendApplicationArgument(mMidletInfo->mMIDletUid.toString());

        // Get the heap size recorded from the previous runs.
        int heapSize = getHeapSize();
        if (heapSize > com_nokia_mj_impl_rt_midp_MemoryLogger_MAX_OLD_SPACE)
        {
            heapSize = com_nokia_mj_impl_rt_midp_MemoryLogger_MAX_OLD_SPACE;
        }

        if (heapSize > com_nokia_mj_impl_rt_midp_MemoryLogger_DEFAULT_OLD_SPACE)
        {
            jvm->overrideOldHeapSize(heapSize / 1024);
        }
    }

    jvm->setMainClass(RUNTIME_MAIN_CLASS);
    jvm->enableThreadDumping();
    jvm->appendSystemProperty(L"-Dcom.nokia.rt.port=midp");
#ifndef RD_JAVA_UI_QT
    jvm->appendSystemProperty(L"-Dcom.nokia.legacy.support=LegacySymbian");
#endif // RD_JAVA_UI_QT

    // Provide access to this object by Java peer via delivering a pointer
    // to this object.
    jvm->appendApplicationArgument(L"-handle");
    MidpStarterInternalSupport* internalSupport = this;
    int handle = reinterpret_cast<int>(internalSupport);
    jvm->appendApplicationArgument(JavaCommonUtils::intToWstring(handle));

    // If the intention is to go to prewarmed state pass the info to Java peer.
    if (mMidletInfo->mPreWarmStart)
    {
        jvm->appendApplicationArgument(L"-prewarm");
        // Captain needs a pid in order to identify the prewarmed runtime.
        jvm->appendApplicationArgument(JavaCommonUtils::intToWstring(getpid()));

        // Starting with lower old space in pre warm case.
        jvm->overrideOldHeapSize(36);

        // In the pre-warm start we need to add something into class path.
        // Otherwise the VM puts class path to point MIDP private data cage.
#ifdef RD_JAVA_SYMBIAN_TARGET
        jvm->appendClassPath(L"z:/private/ignore.jar");
#else // RD_JAVA_SYMBIAN_TARGET
        jvm->appendClassPath(L"./ignore.jar");
#endif // RD_JAVA_SYMBIAN_TARGET
    }

    // If the requested to go to back ground, pass the info to Java peer.
    if (mMidletInfo->mBackGroundRequested)
    {
        jvm->appendApplicationArgument(L"-background");
        jvm->appendApplicationArgument(TRUE_WSTR);
    }

    // If the autoinvocation happened pass the info to Java peer.
    if (mMidletInfo->mAutoInvocationRequested)
    {
        jvm->appendApplicationArgument(L"-autoinvocation");
        jvm->appendApplicationArgument(TRUE_WSTR);
        jvm->appendApplicationArgument(L"-autoInvocationAdditional");
        jvm->appendApplicationArgument(mPushAdditionalInfo);
    }

    // Enable thread dumping. If the captain informed about debug mode
    // or arguments for MIDlet set the arguments provided by the captain.
    std::wstring midletArgs;
    starterUtils->enableDevelopmentFeatures(*jvm.get(),
                                            mMidletInfo->mDebugRequested || mMidletInfo->mMIDletHasArgs,
                                            &midletArgs);

    if (midletArgs.length() == 0)
    {
        // If MIDlet was waiting for push or was pre-warmed,
        // the arguments can be already in midlet info
        midletArgs = mMidletInfo->mMIDletArgs;
    }

    if (midletArgs.length() > 0)
    {
        // Pass the arguments to 'Java side' in encoded form.
        // Encoding is done to prevent security risks.
        std::wstring encodedArgs = L"-Dcom.nokia.mid.cmdline=";

        encodedArgs.append(encodeArgs(midletArgs));

        jvm->appendSystemProperty(encodedArgs);
        // remember the args
        mMidletInfo->mMIDletArgs = encodedArgs;
    }

    // When starting this property has always value "1"
    jvm->appendSystemProperty(L"-Dcom.nokia.mid.cmdline.instance=1");

    // There might be extensions available. Solving if those exist.
    std::wstring extendedBootClassPath;
    // This call is platform dependent.
    starterUtils->getExtBootClassPath(extendedBootClassPath);
    if (extendedBootClassPath.length() > 0)
    {
        // Append the extensions to bootclasspath.
        jvm->appendBootClassPath(extendedBootClassPath);

        // Provide the bootclasspath also as a system propery
        // for solving the protected and restricted packages.
        std::wstring addOnList(L"-Dcom.nokia.mj.addon.list=");
        addOnList += extendedBootClassPath;
        jvm->appendSystemProperty(addOnList);
    }

    mRuntimeState = Active;

    // Start the JVM.
    int status = jvm->startJvm();

    if (mComms.get())
    {
        mComms->disconnect();
    }
    CoreUi::releaseUi(coreUiLoader);

    // Close the thread that ensures the exit.
    if (mExitMonitor.get())
    {
        mShudownOk = true;
        LOG(EJavaRuntime, EInfo, "Notifying exit thread.");
        mExitMonitor->notify();
    }

    return status;
}


void MidpRuntimeStarter::parseArgs(int argc, char* argv[])
{
    JELOG2(EJavaRuntime);

    bool appUidFound = false;

    // Loop through the arguments. First one can be skipped, because it
    // contains the name of the executable.
    for (int index = 1 ; index < argc; index++)
    {
        const char* key = argv[index];
        const char* value = 0;

        // Checking if there is value available.
        if (index+1 < argc)
        {
            value = argv[index+1];
        }
        LOG2(EJavaRuntime, EInfo,"MidpRuntimeStarter::parseArgs(). "
             "Handling key %s with value %s",
             key,
             value==0?"<No Value>":value);

        if (strcmp(key, APP_UID_ARGUMENT) == 0)
        {
            if (value)
            {
                mMidletInfo->mMIDletUid =
                    Uid(java::util::JavaCommonUtils::utf8ToWstring(value));

                appUidFound = true;
                index++;
            }
        }

        else if (strcmp(key, PREWARM_ARGUMENT) == 0)
        {
            mMidletInfo->mPreWarmStart = true;
            LOG1(EJavaRuntime, EInfo,"PREWARM_ARGUMENT = %d ",  mMidletInfo->mPreWarmStart);
        }

        else if (strcmp(key, PUSH_ARGUMENT) == 0)
        {
            mMidletInfo->mPushStart = true;
            LOG1(EJavaRuntime, EInfo,"PUSH_ARGUMENT = %d ",  mMidletInfo->mPushStart);
        }

        else if (strcmp(key, AUTO_INVOCATION_ARGUMENT) == 0)
        {
            mMidletInfo->mAutoInvocationRequested = true;
            LOG1(EJavaRuntime, EInfo,"AUTO_INVOCATION_ARGUMENT = %d ",  mMidletInfo->mAutoInvocationRequested);
        }

        else if (strcmp(key, DEBUG_ARGUMENT) == 0)
        {
            mMidletInfo->mDebugRequested = true;
            LOG1(EJavaRuntime, EInfo,"DEBUG_ARGUMENT = %d ",  mMidletInfo->mDebugRequested);
        }

        else if (strcmp(key, EXTRA_ARGUMENTS) == 0)
        {
            mMidletInfo->mMIDletHasArgs = true;
            LOG1(EJavaRuntime, EInfo,"EXTRA_ARGUMENTS = %d ",  mMidletInfo->mMIDletHasArgs);
        }

        else if (strcmp(key, BG_START_ARGUMENT) == 0)
        {
            mMidletInfo->mBackGroundRequested = true;
            LOG1(EJavaRuntime, EInfo,"BG_START_ARGUMENT = %d ",  mMidletInfo->mBackGroundRequested);
        }

        else
        {
            std::string errorStr("Unknown argument received: ");
            errorStr.append(key);
            throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
        }
    }

    if (appUidFound == false && mMidletInfo->mPreWarmStart == false)
    {
        std::string errorStr("Argument ");
        errorStr.append(APP_UID_ARGUMENT);
        errorStr.append(" not defined!");
        throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
    }
}

void MidpRuntimeStarter::startCoreUi(std::auto_ptr<java::util::DynamicLibLoader>& coreUiLoader)
{
    CoreUi& coreUi = CoreUi::getUiInstance(coreUiLoader);

    // Create the default UI only if not going into pre-warmed state.
    if (!mMidletInfo->mPreWarmStart)
    {

        // Open a session to JavaStorage.
        std::auto_ptr<JavaStorage> javaStorage(JavaStorage::createInstance());
        javaStorage->open();

        CoreUiParams uiParams;

        // Get the MIDlet suite UID from storage.
        if (mMidletInfo->mMIDletSuiteUid.toString() == L"")
        {
            getMIDletSuiteUidFromStorage(*javaStorage.get());
        }

        // Check if the MIDlet has defined the Nokia-MIDlet-App-Orientation
        // JAD attribute.
        std::auto_ptr<std::wstring> appOrientation
        (getMidletAttributeFromStorage(*javaStorage.get(),
                                       L"Nokia-MIDlet-App-Orientation"));
        if (appOrientation.get() != 0)
        {

            std::transform(appOrientation->begin(), appOrientation->end(),
                           appOrientation->begin(), tolower);
            if (*appOrientation == L"portrait")
            {
                uiParams.setOrientation(PORTRAIT);
            }
            else if (*appOrientation == L"landscape")
            {
                uiParams.setOrientation(LANDSCAPE);
            }
            else
            {
                WLOG1(EJavaRuntime, "appOrientation contained unknown value: %S",
                      appOrientation->c_str());
            }
        }
        else
        {
            LOG(EJavaRuntime, EInfo, "Nokia-MIDlet-App-Orientation not defined");
        }

        // Check if the MIDlet has defined the Nokia-MIDlet-Splash-Screen-Image
        // JAD attribute.
        std::auto_ptr<std::wstring> splashScreen
        (getMidletAttributeFromStorage(*javaStorage.get(),
                                       L"Nokia-MIDlet-Splash-Screen-Image"));
        if (splashScreen.get() != 0)
        {
            std::transform(splashScreen->begin(), splashScreen->end(),
                           splashScreen->begin(), tolower);
            if (*splashScreen == L"suppress")
            {
                // If Nokia-MIDlet-Splash-Screen-Image JAD attribute is suppress then
                // we start the UI into background.
                LOG(EJavaRuntime, EInfo, "Nokia-MIDlet-Splash-Screen-Image is suppress");
                uiParams.setScreenMode(NO_START_SCREEN);
                uiParams.setBackgroundStart(true);
            }
            else
            {
                // If Nokia-MIDlet-Splash-Screen-Image JAD attribute is not suppress then
                // we need to solve the root path of the MIDlet and provide that
                // to the coreUI.
                uiParams.setScreenMode(MIDLET_DEFINED_SCREEN);
                LOG1(EJavaRuntime, EInfo, "Nokia-MIDlet-Splash-Screen-Image, setPath to %S",
                     mMidletInfo->mMIDletRootPath.c_str());
            }
        }
        else
        {
            LOG(EJavaRuntime, EInfo, "Nokia-MIDlet-Splash-Screen-Image not defined");
            uiParams.setScreenMode(DEFAULT_START_SCREEN);
        }

        getRootPath();
        uiParams.setImagePath(mMidletInfo->mMIDletRootPath);

        // If there was a background start requst pass the info to coreUi.
        // Also the autoinvocation start puts the screen into BG.
        if (mMidletInfo->mBackGroundRequested || mMidletInfo->mAutoInvocationRequested)
        {
            uiParams.setBackgroundStart(true);
        }

        // Start the coreUI.
        JavaOsLayer::startUpTrace("Starting CoreUI", -1, -1);
        coreUi.start(mMidletInfo->mMIDletUid, &uiParams);
        JavaOsLayer::startUpTrace("CoreUI started", -1, -1);

        getMIDletSuiteInfoFromStorage(javaStorage.get(), JAR_PATH,
                                      mMidletInfo->mClassPath);
    }

}

void MidpRuntimeStarter::
ApplicationStateChangeRequest(ApplicationState state)
{
    JELOG2(EJavaRuntime);
    LOG1(EJavaRuntime, EInfo, "ApplicationStateChangeRequest: %d", state);
    switch (state)
    {
    case START_APPLICATION:
    case CONTINUE_APPLICATION_STARTUP:
        doStateChange(Start);
        break;

    case CLOSE_APPLICATION:
        doStateChange(Stop);
        break;

    default:
        std::string errorStr("Illegal state ");
        errorStr.append(JavaCommonUtils::intToString(state));
        throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
    }
}

void MidpRuntimeStarter::doStateChange(StateChangeRequest request)
{
    JELOG2(EJavaRuntime);
    LOG2(EJavaRuntime, EInfo, "MidpRuntimeStarter::doStateChange. Request: %d,"
         " current state: %d", request, mRuntimeState);
    ScopedLock lock(mProcessesMutex); // Making the method thread safe
    switch (request)
    {
    case Start:
        // Check if we are still waiting start command.
        if (mRuntimeState == PushListen)
        {
            mRuntimeState = Active;
            //Allow application startUp.
            mMonitor->notify();
        }
        break;

    case Stop:
        // Check if we are still waiting start command.
        if (mRuntimeState == PushListen)
        {
            //Allow application to close.
            mRuntimeState = Closing;
            mMonitor->notify();
        }
        break;

    default:
        std::string errorStr("Illegal request ");
        errorStr.append(JavaCommonUtils::intToString(request));
        throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
    }

}

void* MidpRuntimeStarter::ensureExit(void* ptr)
{
    JELOG2(EUtils);
#ifdef __SYMBIAN32__
    RThread().SetPriority(EPriorityMore);
#endif // __SYMBIAN32__
    MidpRuntimeStarter* starter = reinterpret_cast<MidpRuntimeStarter*>(ptr);
    LOG(EJavaRuntime, EInfo, "Starting to wait for the shutdown.");
    const int waitTime = 1000; // 1 second.
    starter->mExitMonitor->wait(waitTime);
    LOG(EJavaRuntime, EInfo, "woke up from monitor.");
    if (!starter->mShudownOk)
    {
        LOG(EJavaRuntime, EInfo, "Killing process.");
#ifdef __SYMBIAN32__
        RProcess().Kill(0);
#else
        kill(getpid(), SIGTERM);
#endif // __SYMBIAN32__
    }
    LOG(EJavaRuntime, EInfo, "Clean exit.");
    return 0;
}

void MidpRuntimeStarter::closeRuntimeInd()
{
    JELOG2(EJavaRuntime);
    LOG(EJavaRuntime, EInfo, "Starter got close indication from JVM");

    // Create a thread for ensure the exit of the process, if something goes
    // wrong.
    pthread_t tid;
    mExitMonitor.reset(Monitor::createMonitor());
    pthread_create(&tid, 0, ensureExit, this);

    if (mMidletInfo->mPushStart)
    {
        closePush();
    }

}

void MidpRuntimeStarter::setUids(const Uid& midletUid, const Uid& midletSuiteUid)
{
    JELOG2(EJavaRuntime);
    mMidletInfo->mMIDletUid = midletUid;
    mMidletInfo->mMIDletSuiteUid = midletSuiteUid;
    LOG2(EJavaRuntime, EInfo, "Setting UID set during pre-warm start: m=%S, s=%S",
         mMidletInfo->mMIDletUid.toString().c_str(),
         mMidletInfo->mMIDletSuiteUid.toString().c_str());
}

int MidpRuntimeStarter::getHeapSize() const
{
    JELOG2(EJavaRuntime);
    // No need to do charcter conversion in here.
    std::string root(mMidletInfo->mClassPath.begin(),
                     mMidletInfo->mClassPath.end());
    int heapSize = -1;

    // The file is stored into same location where the jar file is put.
    // replace the 'xxx.jar' with 'heap' and open the file.

    size_t pos = root.find_last_of("/\\");
    if (pos != std::string::npos)
    {
        root.erase(pos+1);
        root += "heap";
        LOG1(EJavaRuntime, EInfo, "Heap size from file %s.", root.c_str());
        try
        {
            std::ifstream heapFile;
            heapFile.open(root.c_str(), std::ifstream::in);
            heapFile >> heapSize;
            heapFile.close();
            LOG1(EJavaRuntime, EInfo, "  heap: %d.", heapSize);
        }
        catch (std::exception& e)
        {
            LOG2(EJavaRuntime, EInfo, "Not able to read from file %s. Error %s",
                 root.c_str(), e.what());
        }
    }
    return heapSize;
}

std::wstring MidpRuntimeStarter::encodeArgs(const std::wstring& str)
{
    // Encode each 16 bit (or 8 bit) character to 4 (or 2) safe characters to prevent
    // possible security problems when this string is passed as an command line
    // system property parameter to JVM
    std::wstring res;
    int len = str.length();

    for (int pos = 0; pos < len; ++pos)
    {
        wchar_t c = str[pos];
        if (c & 0xFF00)
        {
            // 16 bit char, must send all bits
            res += ( L'A' + (c >> 12) );
            res += ( L'A' + ((c & 0x0F00) >> 8) );
            res += ( L'A' + ((c & 0x00F0) >> 4) );
            res += ( L'A' + ((c & 0x000F)) );
        }
        else
        {
            // 8 bit char, send only lowest 8 bits
            res += ( L'a' + ((c & 0x00F0) >> 4) );
            res += ( L'a' + ((c & 0x000F)) );
        }
    }

    return res;
}

void MidpRuntimeStarter::getMIDletSuiteUidFromStorage(java::storage::JavaStorage& storage)
{
    JELOG2(EJavaRuntime);

    // Get the MIDlet suite UID.
    JavaStorageApplicationEntry_t midletEntries;

    // Reading the MIDlet specific attributes from APPLICATION_TABLE.
    // MIDlet UID is a key.
    storage.read(APPLICATION_TABLE, mMidletInfo->mMIDletUid,
                 midletEntries);
    // Storing UID of the MIDlet suite.
    mMidletInfo->mMIDletSuiteUid = Uid(getDbValue(midletEntries, PACKAGE_ID));
}

void MidpRuntimeStarter::getMIDletSuiteInfoFromStorage(JavaStorage* storageConnection,
        const std::wstring& key,
        std::wstring& value) const
{
    JELOG2(EJavaRuntime);

    // If the JavaStorage connection is created temporarily in this method, auto_ptr is used to
    // ensure closing and destroyng the JavaStorage connection. Don't use javaStorage ptr to any
    // other purposes - instead use storageConnection.
    std::auto_ptr<JavaStorage> javaStorage;
    if (storageConnection == 0)
    {
        javaStorage.reset(JavaStorage::createInstance());
        storageConnection = javaStorage.get();
        javaStorage->open();
    }

    JavaStorageApplicationEntry_t midletSuiteEntries;

    // Reading the MIDlet suite specific attributes from
    // APPLICATION_PACKAGE_TABLE. MIDlet suite UID is a key.
    storageConnection->read(APPLICATION_PACKAGE_TABLE,
                            mMidletInfo->mMIDletSuiteUid,
                            midletSuiteEntries);
    value = getDbValue(midletSuiteEntries, key);
}

std::wstring*
MidpRuntimeStarter::getMidletAttributeFromStorage(JavaStorage& storage,
        const std::wstring& searchKey)
const
{
    JELOG2(EJavaRuntime);

    JavaStorageEntry findPattern;
    JavaStorageApplicationEntry_t findPatterns;
    JavaStorageApplicationList_t foundEntries;

    findPattern.setEntry(ID, mMidletInfo->mMIDletSuiteUid.toString(),
                         JavaStorageEntry::STRING);
    findPatterns.insert(findPattern);

    findPattern.setEntry(NAME, searchKey, JavaStorageEntry::STRING);
    findPatterns.insert(findPattern);

    findPattern.setEntry(VALUE,L"");
    findPatterns.insert(findPattern);

    storage.search(APPLICATION_PACKAGE_ATTRIBUTES_TABLE, findPatterns, foundEntries);

    JavaStorageApplicationList_t::iterator iter = foundEntries.begin();

    if (iter != foundEntries.end())
    {
        JavaStorageEntry findPatternForValue;
        findPatternForValue.setEntry(VALUE, L"");
        JavaStorageApplicationEntry_t::iterator valueIter = iter->find(findPatternForValue);
        if (valueIter != iter->end())
        {
            return new std::wstring(valueIter->entryValue()); // codescanner::nonleavenew
        }
        else
        {
            std::string errorStr("MIDLET with UID: ");
            std::wstring uid = mMidletInfo->mMIDletUid.toString();
            errorStr += std::string(uid.begin(), uid.end());
            errorStr += ". Error reading from storage: ";
            errorStr += std::string(searchKey.begin(), searchKey.end());
            throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
        }
    }
    return 0;
}



std::wstring MidpRuntimeStarter::getDbValue(
    const java::storage::JavaStorageApplicationEntry_t& entry,
    const std::wstring& key) const
{
    JELOG2(EJavaRuntime);
    JavaStorageEntry findPattern;
    std::wstring empty;

    findPattern.setEntry(key, empty);

    // Get attribute from read attributes.
    JavaStorageApplicationEntry_t::const_iterator findIterator =
        entry.find(findPattern);

    if (findIterator != entry.end())
    {
        return findIterator->entryValue();
    }
    else
    {
        std::string errorStr("MIDLET with UID: ");
        std::wstring uid = mMidletInfo->mMIDletUid.toString();
        errorStr += std::string(uid.begin(), uid.end());
        errorStr += ". Not able to find attribute ";
        errorStr += std::string(key.begin(), key.end());
        throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
    }
}

void MidpRuntimeStarter::handlePushStart()
{
    JELOG2(EJavaRuntime);
    mMonitor.reset(Monitor::createMonitor());
    mRuntimeState = PushListen;
    PushControllerStarter::getPushControllerStarter(mPushLib).
    startListen(mMidletInfo->mMIDletUid, this);
    mMonitor->wait();
}

void MidpRuntimeStarter::closePush()
{
    // Received a close message from Java peer.
    PushControllerStarter::getPushControllerStarter(mPushLib).close();
}

void MidpRuntimeStarter::
startMidletRequestFromPush(const std::wstring& pushAdditionalInfo)
{
    JELOG2(EJavaRuntime);
    mPushAdditionalInfo = pushAdditionalInfo;

    // This can be considered to be auto invocation. Setting flag on
    // to be delivered to Java runtime.
    mMidletInfo->mAutoInvocationRequested = true;

    doStateChange(Start);
}

void MidpRuntimeStarter::closeRuntimeRequestFromPush()
{
    JELOG2(EJavaRuntime);
    doStateChange(Stop);
}

void MidpRuntimeStarter::initComms()
{
    JELOG2(EJavaRuntime);
    mComms.reset(new CommsClientEndpoint(L"javacaptain")); // codescanner::nonleavenew
    mComms->registerDefaultListener(this);
    int result = mComms->connect(IPC_ADDRESS_JAVA_CAPTAIN_C);
    if (result != 0)
    {
        std::string errorStr("Connection to JavaCaptain failed: Reason = ");
        errorStr.append(JavaCommonUtils::intToString(result));
        throw RuntimeException(errorStr, __FILE__, __FUNCTION__, __LINE__);
    }
    CommsMessage msg;
    msg.setModuleId(PLUGIN_ID_RTC_C);
    setApplicationRunningIndParams(msg, mMidletInfo->mMIDletUid, 0);
    mComms->send(msg);
}

void MidpRuntimeStarter::processMessage(java::comms::CommsMessage& message)
{
    JELOG2(EJavaRuntime);
    int messageId = message.getMessageId();

    // RTC_MSG_ID_LAUNCH_APPLICATION_REQ and
    // RTC_MSG_ID_TERMINATE_APPLICATION_REQ are received from the Java Captain
    // when we are listening push connection and the VM is not started.
    // When the MIDlet is running normally, these two messages are handled
    // by Java peer.
    // RTC_MSG_ID_ADD_PUSH_CONNECTION_IND is always handled here.

    switch (messageId)
    {
    case RTC_MSG_ID_LAUNCH_APPLICATION_REQ:
    {
        LOG(EJavaRuntime, EInfo, "RTC_MSG_ID_LAUNCH_APPLICATION_REQ");
        // Start the push MIDlet that is pre started to listen mode.

        Uid          uid;
        int          type;
        int          options;
        std::string  rtc;
        std::wstring midletArgs;
        std::wstring runtimeArguments;

        getLaunchApplicationReqParams(message, uid, type, options, rtc,
                                      midletArgs,
                                      runtimeArguments);
        if (type == RTC_LAUNCH_TYPE_AUTO_INVOCATION_C)
        {
            mMidletInfo->mAutoInvocationRequested = true;
        }

        // The arguments will be passed to midlet when JVM is started
        mMidletInfo->mMIDletArgs = midletArgs;

        ApplicationStateChangeRequest(START_APPLICATION);
    }
    break;

    case RTC_MSG_ID_TERMINATE_APPLICATION_REQ:
        LOG(EJavaRuntime, EInfo, "RTC_MSG_ID_TERMINATE_APPLICATION_REQ");

        // Stop the push MIDlet that is pre started to listen mode.
        ApplicationStateChangeRequest(CLOSE_APPLICATION);
        break;

    case RTC_MSG_ID_ADD_PUSH_CONNECTION_IND:
    {
        LOG(EJavaRuntime, EInfo, "RTC_MSG_ID_ADD_PUSH_CONNECTION_IND");
        Uid uidFromMessage;
        getUpdatePushReqParams(message, uidFromMessage);
        if (uidFromMessage == mMidletInfo->mMIDletUid)
        {

            PushControllerStarter::getPushControllerStarter(mPushLib).
            updatePushRegs(mMidletInfo->mMIDletUid, this);
        }
        else
        {
            ELOG2(EJavaRuntime, "getUpdatePushReqParams: wrong UID!: "
                  "%S should be %S",
                  uidFromMessage.toString().c_str(),
                  mMidletInfo->mMIDletUid.toString().c_str());
        }
    }
    break;

    default:
        ELOG1(EJavaRuntime, "Unknown message sent to Runtime %d",
              messageId);
        break;
    }
}

const std::wstring& MidpRuntimeStarter::getRootPath() const
{
    JELOG2(EJavaRuntime);
    // If the root path is not yet cached, read it from storage.
    if (mMidletInfo->mMIDletRootPath.length() == 0)
    {
        getMIDletSuiteInfoFromStorage(0, ROOT_PATH,
                                      mMidletInfo->mMIDletRootPath);
    }
    return mMidletInfo->mMIDletRootPath;
}

const java::util::Uid& MidpRuntimeStarter::getUid() const
{
    return mMidletInfo->mMIDletUid;
}