utilities/serviceipcclient/platform/qt/serviceipclocalsocket.cpp
author hgs
Fri, 15 Oct 2010 17:30:59 -0400
changeset 16 3c88a81ff781
permissions -rw-r--r--
201041

/**
   This file is part of CWRT package **

   Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). **

   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/>.
*/


#include <QByteArray>
#include "serviceipclocalsocket_p.h"

namespace WRT
{
// CONSTANTS
const char* REQUEST_COMPLETE_TOKEN = ";ROK";
const int REQUEST_COMPLETE_TOKEN_LENGTH = 4;
const char REQUEST_DELIMITER_TOKEN = ';';
/*!
 \class ServiceLocalSocketIPC
 QLocalSocket based IPC client-side backend
 */

/*!
 Constructor
 */
ServiceLocalSocketIPC::ServiceLocalSocketIPC() : m_BufferType( ENoBuffer )
{
    m_Socket = new QLocalSocket();
    QObject::connect(m_Socket, SIGNAL( error( QLocalSocket::LocalSocketError ) ),
    this, SLOT( handleError( QLocalSocket::LocalSocketError ) ) );
}

/*!
 Destructor
 */

ServiceLocalSocketIPC::~ServiceLocalSocketIPC()
{
    delete m_Socket;
}

/*!
 Connect to the server
 @param aServerName name of the server to connect to
 @return true if connected, false otherwise
 */
bool ServiceLocalSocketIPC::connect(const QString& aServerName)
{
    bool rtn;
    m_Socket->connectToServer(aServerName);
    rtn = m_Socket->waitForConnected();
    return rtn;
}

/*!
 Disconnect from the server
 */
void ServiceLocalSocketIPC::disconnect()
{
    m_Socket->close();
}

/*!
 Starts the service
 @param aServerName name of the server to connect to
 @param aExeName name of the server executable
 @return true if server started, false otherwise
 */
bool ServiceLocalSocketIPC::startServer(const QString& aServerName,
                                        const QString& aExeName)
{
    bool started(true);

    //Semaphore with 1 initial count,
    //Use system semaphore to ensure no contention exists across multiple processes
    //According to QT documentation for System Semaphores on Windows, 
    //the semaphore is automatically cleaned up if a process crashes, thus preventing deadlock
    //
    QSystemSemaphore funcSem(aServerName + FUNCTIONSEM, 1);
    funcSem.acquire();

    // Shared chunk to check if the server has been started or not
    QSharedMemory sharedMem(aServerName);
    char* data(NULL);
    bool attached = sharedMem.attach();
    if (attached) {
        data = (char*) sharedMem.data();
    }

    // Shared memory not created or the flag was not set properly
    if (!attached || strcmp(data, SERVERNOTSTARTED) == 0) {
        // Create the server wait semaphore. When the server has listened, 
        // it will signaled after listen has started
        //
        QSystemSemaphore sem(aServerName + SERVERSEM, 0);

        // Start the server, since this function is mutex'ed by the global semaphore 
        // only 1 process/thread can reach here
        //
        started = QProcess::startDetached(aExeName);

        // Wait until the server signals
        if (started) {
            sem.acquire();
        }
    }

    // Test if server started successfully
#if _DEBUG
    attached = sharedMem.attach();
    if( attached )
    {
        data = (char*)sharedMem.data();
        if( strcmp( data, SERVERSTARTED ) == 0 )
        {
            qDebug() << "Server Started Successfully";
        }
    }
#endif // _DEBUG
    // Free shared memory
    sharedMem.detach();

    // Release the function semaphore
    funcSem.release(1);
    return started;
}

/*!
 Send a request synchronously
 @param aRequestType type of request, toAscii() will be called to serialize the data
 @param aData aData data to send to the server
 @return true if data is sent, false otherwise
 */
bool ServiceLocalSocketIPC::sendSync(const QString& aRequestType,
                                     const QByteArray& aData)
{
    QByteArray data;
    data.setNum(aData.length());
    data.append(REQUEST_DELIMITER_TOKEN);
    data.append(aRequestType.toAscii());
    data.append(REQUEST_DELIMITER_TOKEN);
    data.append(aData);
    int count = m_Socket->write(data);
    m_Socket->flush();
    m_BufferType = ESyncBuffer;
    return (count > 0);
}

/*!
 Send a request asynchronously
 @param aRequestType type of request, toAscii() will be called to serialize the data
 @param aData data to send to the server
 */
void ServiceLocalSocketIPC::sendAsync(const QString& aRequestType,
                                      const QByteArray& aData)
{
    QByteArray data;
    data.setNum(aData.length());
    data.append(REQUEST_DELIMITER_TOKEN);
    data.append(aRequestType.toAscii());
    data.append(REQUEST_DELIMITER_TOKEN);
    data.append(aData);
    m_Socket->write(data);

    // Connect the signal and reset aync data buffer
    m_AsyncData.clear();
    QObject::connect(m_Socket, SIGNAL( readyRead() ),
    this, SLOT( handleReadyRead() ) );
    m_BufferType = EAsyncBuffer;
}

/*!
 Reads all data pending in the buffer
 @return QByteArray data that has been read
 */
QByteArray ServiceLocalSocketIPC::readAll()
{
    QByteArray result;

    // If asynchronous read all data from the socket 
    //
    if ( m_BufferType == ESyncBuffer ) {
        // Wait for all data to be completed before returning
        //
        bool done = false;
        do {
            result.append(m_Socket->readAll());
            if (result.right(REQUEST_COMPLETE_TOKEN_LENGTH)
                            == REQUEST_COMPLETE_TOKEN) {
                // Chop the end token
                result.chop(REQUEST_COMPLETE_TOKEN_LENGTH);
                done = true;
            }
        } while (done == false);
    }
    // If async, return the internal databuffer
    else if( m_BufferType == EAsyncBuffer ){
        // Should be just a d-ptr copy
        result = m_AsyncData;
        QObject::disconnect(m_Socket, SIGNAL( readyRead() ),
        this, SLOT( handleReadyRead() ) );
    }
    m_BufferType = ENoBuffer;

    return result;
}

/*!
 Waits until data is available for reading 
 @return bool true if data can be read
 */
bool ServiceLocalSocketIPC::waitForRead()
{
    return m_Socket->waitForReadyRead();
}

/*!
 Handle any socket errors
 @param socketError error
 */
void ServiceLocalSocketIPC::handleError(QLocalSocket::LocalSocketError aSocketError)
{
    // Use base class to send this
    emitError(doMapErrors(aSocketError));
}

/*!
 Handle when data is ready to be read
 */
void ServiceLocalSocketIPC::handleReadyRead()
{
    m_AsyncData.append(m_Socket->readAll());
    if (m_AsyncData.right(REQUEST_COMPLETE_TOKEN_LENGTH)
                    == REQUEST_COMPLETE_TOKEN) {
        // Chop the end token
        m_AsyncData.chop(REQUEST_COMPLETE_TOKEN_LENGTH);

        // Use base class to send signal when all the data has been assembled
        emitReadyRead();
    }
}

int ServiceLocalSocketIPC::doMapErrors( int aError )
{
    int error(0);
    
    // Map QT Local Socket error codes to custom error codes
    //
    switch( aError ) {
    case QLocalSocket::ConnectionError:
    case QLocalSocket::ConnectionRefusedError: {
        error = ServiceFwIPC::EConnectionError;
        break;
        }
    case QLocalSocket::PeerClosedError: {
        error = ServiceFwIPC::EConnectionClosed;
        break;
        } 
    case QLocalSocket::ServerNotFoundError: {
        error = ServiceFwIPC::EServerNotFound;
        break;
        } 
    case QLocalSocket::SocketAccessError:
    case QLocalSocket::SocketResourceError:
    case QLocalSocket::SocketTimeoutError:
    case QLocalSocket::DatagramTooLargeError:
    case QLocalSocket::UnsupportedSocketOperationError: {
        error = ServiceFwIPC::EIPCError;
        break;
        }    
    case QLocalSocket::UnknownSocketError: {
        error = ServiceFwIPC::EUnknownError;
        break;
        }
    }
    return error;
}

}
// END OF FILE