qthighway/xqserviceutil/src/xqservicemanager.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 16 Apr 2010 15:51:22 +0300
changeset 1 2b40d63a9c3d
child 4 90517678cc4f
permissions -rw-r--r--
Revision: 201011 Kit: 201015

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1 of the License.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, 
* see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
*
* Description:                                                         
*
*/

#include "xqservicelog.h"
#include <xqserviceglobal.h>

#include "xqrequestutil.h"

#include "xqservicemanager.h"
#include "xqserviceipcconst.h"
#include <QCoreApplication>
#include <e32std.h>
#include <w32std.h>

#include <apparc.h>
#include <apgcli.h>
#include <apacln.h>
#include <apgtask.h>
#include <barsread2.h>
#include <apacmdln.h>
#include <coemain.h>

#include <QBuffer>
#include "xqservicemetadata/xqservicemetadata_p.h"
#include <xqservicemetadata/xqaiwinterfacedescriptor.h>

#include <QString>


class XQServiceManagerPrivate 
    {
    public:
        XQServiceManagerPrivate() {iLatestError = 0;};
        ~XQServiceManagerPrivate() {};

        enum matchMode
        {
            MatchInterfaceName,
            MatchServiceAndInterfaceName
        };
        
        int StartServer(const QString& aService,  bool embedded, int& applicationUid, quint64& processId,
                       XQRequestUtil *util);
        TInt Discover(const QString& aService,TUid& aAppUid, QList<XQAiwInterfaceDescriptor>& interfaces, int matchMode);
        int  LatestError() const {return iLatestError;};
        
    private:
        void StartServerL(const TUid& uid, bool embedded, TUint64& processId, XQRequestUtil *util);
        TInt Discover(const TDesC& aService,TUid& aAppUid, QList<XQAiwInterfaceDescriptor>& interfaces, int matchMode);
        CApaAppServiceInfoArray* AvailableServiceImplementationsL();
        TUint64 getAppPid(const TUid& aAppUid);   
        int doMapErrors(TInt aError);
        
        TVersion iVersion;
        TApaAppInfo  iAppInfo;
        int iLatestError;
    };

XQServiceManager::XQServiceManager()
{
    XQSERVICE_DEBUG_PRINT("XQServiceManager::XQServiceManager");
    d = new XQServiceManagerPrivate();
}

XQServiceManager::~XQServiceManager()
{
    XQSERVICE_DEBUG_PRINT("XQServiceManager::~XQServiceManager");
    delete d;
}

/*!
    Starts service
    \param service The full name of service (servicename + interfacename)
    \param embedded Start in embedded mode
    \param applicationUid Returned applicatiion
    \return List of implementations
*/
int XQServiceManager::startServer(const QString& service, bool embedded, int& applicationUid, quint64& threadId)
{
    XQSERVICE_DEBUG_PRINT("XQServiceManager::startServer(1)");
    return startServer(service,embedded,applicationUid,threadId,NULL);
}

int XQServiceManager::startServer(const QString& service, bool embedded, int& applicationUid, quint64& threadId,
                                 const void *userData)
{
    XQSERVICE_DEBUG_PRINT("XQServiceManager::startServer(2)");

    // The "XQServiceRequest::send(QVariant& retData)" function passed the utility as user data
    // the IPC layer. Could be NULL if no descriptor was given upon creating the request.
    // The util data is known to be writeable (though passed as const userData)
    XQRequestUtil *util = static_cast<XQRequestUtil*>((void *)userData);
    if (util == 0)
    {
        // Something is badly wrong as this should be always avaible
        return XQService::EMgrInternalError;
    }
    
    return d->StartServer(service,embedded,applicationUid,threadId, util);
}


/*!
    Finds implementations for the given interface
    \param interfaceName Interfacename to match
    \return List of implementations
*/
QList<XQAiwInterfaceDescriptor>  XQServiceManager::findInterfaces ( const QString &interfaceName ) const
    {
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::findInterfaces");
    QList<XQAiwInterfaceDescriptor> interfaces;
    TUid appUid;
    interfaces.clear();
    TInt error=d->Discover(interfaceName, appUid, interfaces, XQServiceManagerPrivate::MatchInterfaceName);
    return interfaces;
    }

/*!
    Finds implementations for the given interface implemented by given service
    \param serviceName Service name
    \param interfaceName Interfacename to match
    \return List of implementations
*/
QList<XQAiwInterfaceDescriptor>  XQServiceManager::findInterfaces ( const QString &serviceName, const QString &interfaceName ) const
{
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::findInterfaces 2");
    QList<XQAiwInterfaceDescriptor> interfaces;
    TUid appUid;
    interfaces.clear(); 
    // Catenate to get full name
    TInt error=d->Discover(serviceName + "." + interfaceName, appUid, interfaces,
                           XQServiceManagerPrivate::MatchServiceAndInterfaceName);
    return interfaces;
}


/*!
    Returns the latest error occured
*/
int XQServiceManager::latestError() const
{
    return d->LatestError();
}

// aService is here the full name (service + interface)
int XQServiceManagerPrivate::StartServer(const QString& aService,  bool embedded, int& applicationUid, quint64& processId,
                                        XQRequestUtil *util)
{
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::startServer(2)");
    XQSERVICE_DEBUG_PRINT("aService: %s, embedded: %d", qPrintable(aService), embedded);
    
    TUid appUid;
    appUid.iUid=0;
    
    TInt error = KErrNone;
    QList<XQAiwInterfaceDescriptor> interfaces;
    TPtrC serverName( reinterpret_cast<const TUint16*>(aService.utf16()) );
    if (util->mDescriptor.isValid()) 
    {
        appUid.iUid = util->mDescriptor.property(XQAiwInterfaceDescriptor::ImplementationId).toInt();
        XQSERVICE_DEBUG_PRINT("ApplicationUid from descriptor: %x", appUid.iUid);
    }
    
    // Need to discover service first if descriptor did not contained valid UID
    // Otherwise, go directly starting the service server
    if (appUid.iUid == 0)
    {
        error = Discover(serverName,appUid,interfaces, XQServiceManagerPrivate::MatchServiceAndInterfaceName);
    }
    if (error)
        {
        return doMapErrors(error);
        }

    TRAP(error, StartServerL(appUid,embedded,processId, util));
    applicationUid = appUid.iUid ;

    XQSERVICE_DEBUG_PRINT("ApplicationUid: %x, processId: %d", applicationUid, processId);
    XQSERVICE_DEBUG_PRINT("error: %d", error);
    return doMapErrors(error);
}

void XQServiceManagerPrivate::StartServerL(const TUid& uid, bool embedded, TUint64& processId,
                                           XQRequestUtil *util)
{
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::StartServerL");
    Q_UNUSED(util);  // Currently not used yet.  The uid was set by calling function above
    
    RApaLsSession apa;
    User::LeaveIfError( apa.Connect() );
    CleanupClosePushL( apa );


    bool toBackground = false;
    // Apply the utility's option for embedding 
    embedded = util->mInfo.isEmbedded();
    toBackground = util->mInfo.isBackground();
    
    XQSERVICE_DEBUG_PRINT("\tembedded got from utility=%d", embedded);
    XQSERVICE_DEBUG_PRINT("\tbackground got from utility=%d", toBackground);

    // retrieve application information
    User::LeaveIfError( apa.GetAppInfo( iAppInfo, uid ) );

    TApaAppCapabilityBuf caps;
    User::LeaveIfError(apa.GetAppCapability(caps, uid));
    if (!toBackground)
    {
        // If service wants to be launched to background.. respect it
        toBackground = caps().iLaunchInBackground;
        XQSERVICE_DEBUG_PRINT("\tbackground from apparch=%d", toBackground);
    }

    // Consistency check
    if (embedded && toBackground)
    {
        User::Leave(KErrArgument);
    }

    TFindServer find( iAppInfo.iCaption );
    TFullName fullName;
    TInt err = find.Next( fullName );
    XQSERVICE_DEBUG_PRINT("err: %d", err);
    if ( err != KErrNone )
        {
        CApaCommandLine* cmdLine = CApaCommandLine::NewLC();
        cmdLine->SetExecutableNameL( iAppInfo.iFullName );
        TApaCommand  aLaunchCommand = toBackground ? EApaCommandBackground : EApaCommandRun;
        cmdLine->SetCommandL(aLaunchCommand);

        // just use the Secure ID as server differentiator
        //cmdLine->SetServerRequiredL( uid.iUid );
        RProcess client;
        cmdLine->SetServerRequiredL(client.Id().Id());
        if (embedded) {
            CCoeEnv* env= CCoeEnv::Static();
            if (env)  // Could be NULL 
            {
                RWindowGroup& wg = env->RootWin();
                wg.AllowProcessToCreateChildWindowGroups(iAppInfo.iUid);
                TInt parentId = wg.Identifier();
                if (parentId)
                {
                    // pass our window group ID to the embedded child process
                    cmdLine->SetParentWindowGroupID(parentId);
                    XQSERVICE_DEBUG_PRINT("\tParent ID %x set for %x (%x)", (int)parentId, iAppInfo.iUid, uid.iUid);
                }
            }
            else
            {
                // Can not be embedded (non GUI client)
                embedded = false;
                util->mInfo.setEmbedded(false);
            }
        }
         
        TRequestStatus requestStatusForRendezvous;
        
        // start application with command line parameters
        //User::LeaveIfError( apa.StartApp( *cmdLine, threadId, &requestStatusForRendezvous) );
        QString startupArgs = QString::fromLatin1(XQServiceUtils::StartupArgService);
        if (embedded)
        {
            startupArgs += (" " + QString::fromLatin1(XQServiceUtils::StartupArgEmbedded));
        }
        XQSERVICE_DEBUG_PRINT("\tStartupArgs:%s", qPrintable(startupArgs));
        TPtrC cmdLineArgs( reinterpret_cast<const TUint16*>(startupArgs.utf16()) );
        
        RProcess newApp;
        XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::Creating process");
        User::LeaveIfError(newApp.Create(iAppInfo.iFullName, cmdLineArgs));
        CleanupClosePushL(newApp);
        
        XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::Created process");
        cmdLine->SetProcessEnvironmentL(newApp);
        TProcessId newAppId = newApp.Id();
        processId = newAppId.Id();
        XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::Rendezvous for %x", processId);
        newApp.Rendezvous(requestStatusForRendezvous);  // Asynchronous
        newApp.Resume();
        
        User::WaitForRequest( requestStatusForRendezvous ); // Make the  rendezvouz
        XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::Rendezvous done %d", requestStatusForRendezvous.Int());

        User::LeaveIfError( requestStatusForRendezvous.Int()); 
        CleanupStack::PopAndDestroy(2,cmdLine); // newApp, cmdLine
        
        XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::Done"); 
        }
    
    CleanupStack::PopAndDestroy( &apa ); // ap
}


TUint64 XQServiceManagerPrivate::getAppPid(const TUid& aAppUid)
{
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::getAppPid");
    TUint64 pid = 0;
    // Get the current task
    RWsSession ws;
    if (ws.Connect()==KErrNone) {
        XQSERVICE_DEBUG_PRINT("Connected to window server");
        TApaTaskList tasklist( ws );
        TApaTask task = tasklist.FindApp( aAppUid );        
        if (task.Exists()) {
            XQSERVICE_DEBUG_PRINT("Application found");
            pid=task.ThreadId().Id();
        }
    }
    return pid;
}
CApaAppServiceInfoArray* XQServiceManagerPrivate::AvailableServiceImplementationsL()
{
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::AvailableServiceImplementationsL");
    RApaLsSession ls;
    User::LeaveIfError( ls.Connect() );
    CleanupClosePushL( ls );
    // retrieve list of available services implementations from apparc
    CApaAppServiceInfoArray* apaInfo = 
                    ls.GetServiceImplementationsLC(TUid::Uid(KXQServiceUid));
    CleanupStack::Pop( apaInfo );
    CleanupStack::PopAndDestroy( &ls );
    return apaInfo;
}

TInt XQServiceManagerPrivate::Discover(const QString& aService,TUid& aAppUid, QList<XQAiwInterfaceDescriptor>& interfaces,
                                      int matchMode)
    {
    TPtrC serverName( reinterpret_cast<const TUint16*>(aService.utf16()) );
    TInt error=Discover(serverName, aAppUid, interfaces, matchMode);
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::Discover (1)");
    return error;
    }


TInt XQServiceManagerPrivate::Discover( const TDesC& aService,
                                      TUid& aAppUid, QList<XQAiwInterfaceDescriptor>& interfaces,
                                      int matchMode)
    {
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::Discover (2)");
        
    CApaAppServiceInfoArray* apaInfo = NULL;
    TInt error = KErrNone;
    TRAP(error, apaInfo = AvailableServiceImplementationsL());
    XQSERVICE_DEBUG_PRINT("error: %d", error);
    if (error)
        {
        return error;  // This is fatal as nothing found
        }
    TArray<TApaAppServiceInfo> implArray( apaInfo->Array() );
    XQSERVICE_DEBUG_PRINT("implArray.Count(): %d", implArray.Count());
    if ( !implArray.Count() )
        {
        delete apaInfo;
        return KErrNotFound;   // No services found
        }
    
    TBool found( EFalse );
    QString serviceName = QString::fromUtf16(aService.Ptr(),aService.Length());
    XQSERVICE_DEBUG_PRINT("serviceName: %s", qPrintable(serviceName));
    for ( TInt ii = 0; ii < implArray.Count(); ii++ )  
        {

        //
        // Reset error for each check round
        //
        error = KErrNone;
            
        TUid uid = implArray[ii].Uid();
        XQSERVICE_DEBUG_PRINT("implArray[%d].UID=%x", ii, uid);
            
        RResourceReader res;
        // read opaque data
        TRAP(error,res.OpenL( implArray[ii].OpaqueData() ) )
        if ( error )
            {
            XQSERVICE_DEBUG_PRINT("OpaqueData error: %d", error);
            delete apaInfo;
            apaInfo = NULL;
            continue;  // Next could be OK
            }
        
        //the first WORD contains the number of elements in the resource
        int count = 0;
        TRAP(error,count = res.ReadInt16L());
        if ( error )
            {
            XQSERVICE_DEBUG_PRINT("resource error1: %d", error);
            res.Close();
            delete apaInfo;
            apaInfo = NULL;
            continue;  // Next could be OK
            }      
        QByteArray xmlConf ;
        XQSERVICE_DEBUG_PRINT("resource line count: %d", count);
        for (int i=0; i < count ; i++) 
            {
            TPtrC16 xmlBuf;
            TRAP(error, xmlBuf.Set( res.ReadTPtrC16L() ));
            if (error)
                {
                XQSERVICE_DEBUG_PRINT("resource error2: %d", error);
                res.Close();
                delete apaInfo;
                apaInfo = NULL;
                break;
                }
            else
                {
                QString str=QString::fromUtf16(xmlBuf.Ptr(),xmlBuf.Length());
                XQSERVICE_DEBUG_PRINT("resource str: %s", qPrintable(str));
                xmlConf.append(str.toAscii());
                }
            }

        // If we stop loop for the first resource error,
        // it will hit cases where same interface has been implemented by multiple services
        // So go on checking all the resource files
        if (error)
        {
            continue; // Next could be OK
        }
        
        XQSERVICE_DEBUG_PRINT("resource data: %s", xmlConf.constData());
        QBuffer buf(&xmlConf);
        ServiceMetaData* metaData = new ServiceMetaData(&buf);
        if (metaData->extractMetadata()) 
            {
            ServiceMetaDataResults results=metaData->parseResults();
            // interfaces = results.interfaces;  // return value set here ???!!

            // Go through all interfaces and pick the UI for the first matching one.
            // THIS NEED TO BE FIXED IF SOMEONE WANTS DEDICATED IMPLEMENTATION
            // Fill in the implementationId for all interfaces
            TBool firstUidPicked(EFalse);
            foreach (XQAiwInterfaceDescriptor interface,results.interfaces)
                {
                QString sn;
                if (results.version == ServiceMetaDataResults::VERSION_1)
                    {
                    // Old version of the XML format. The parser took care of adaptation
                    // discovery-name = service-name + interface name
                    XQSERVICE_DEBUG_PRINT("version 1");
                    }
                else
                    {
                    // discovery-name = interface name
                   XQSERVICE_DEBUG_PRINT("version 2");
                    }
                // This is the name used in match
                // TODO: Version handling support: Take the latest version if multiple matches
                switch (matchMode)
                {
                    case MatchInterfaceName :
                        sn = interface.interfaceName();
                        break;
                    case MatchServiceAndInterfaceName :
                        sn =interface.serviceName() + "." + interface.interfaceName();
                        break;
                    default:
                        sn = interface.interfaceName();
                        break;
                }

                XQSERVICE_DEBUG_PRINT("compare name is: %s", qPrintable(sn));
                XQSERVICE_DEBUG_PRINT("requested name: %s", qPrintable(serviceName));
                if (!serviceName.compare(sn,Qt::CaseInsensitive)) 
                    {
                    if (!firstUidPicked)
                        {
                        aAppUid = implArray[ii].Uid();
                        firstUidPicked = ETrue;
                        }
                    XQSERVICE_DEBUG_PRINT("Service found %x", aAppUid);
                    //  Add impl. UID to interface
                    interface.setProperty(XQAiwInterfaceDescriptor::ImplementationId, (int)aAppUid.iUid);
                    found = ETrue;

                    // Add the matched interface to result set
                    interfaces.append(interface);
                    
                    }
                } // forearch interface
            } 
        else
            {
             error = metaData->getLatestError();
             XQSERVICE_DEBUG_PRINT("metadata error: %d", error); 
            }  
        
        delete metaData;
        metaData = NULL;
        res.Close();
        
        } // for implArray ...
    
    delete apaInfo;
    if (!found)
        {
        error = KErrNotFound;
        }
    
    XQSERVICE_DEBUG_PRINT("Discover error: %d", error);
    
    return error;
}

int XQServiceManagerPrivate::doMapErrors(TInt aError)
{
    XQSERVICE_DEBUG_PRINT("XQServiceManagerPrivate::doMapErrors");
    XQSERVICE_DEBUG_PRINT("aError: %d", aError);
    int error(XQService::ENoError);
    
    switch (aError)
    {
    case KErrNone: {
        error = XQService::ENoError;
        break;
    }
    case KErrPermissionDenied:
    case KErrServerTerminated: {
        error = XQService::EConnectionClosed;
        break;
    }
    case KErrServerBusy: {
        error = XQService::EConnectionError;
        break;
    }
    case KErrArgument:
    {
        error = XQService::EArgumentError;
        break;
    }
        
    case KErrNoMemory: {
        error = XQService::EIPCError;
        break;
    }
    case KErrNotFound: {
        error = XQService::EServerNotFound;
        break;
    }

    default:
    {

        if (aError >= XQService::EMetaNoService && aError <= XQService::EMetaDuplicatedCustomKey)
        {
            iLatestError = error;
            return error;  // Already real error
        }
        
        error = XQService::EUnknownError;
        break;
    }
    }

    // Save error
    iLatestError = error;
    
    XQSERVICE_DEBUG_PRINT("error: %d", error);
    return error;
}