locationrequestmgmt/locationserver/src/EPos_CPositionRequest.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:35:25 +0100
branchRCL_3
changeset 55 c92d4f3c47c5
parent 54 0c2046328c85
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201027 Kit: 201035

// 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:
//



// INCLUDE FILES
#include <e32base.h>
#include <lbspositioninfo.h>
#include <lbs/epos_cpositioner.h>
#include <lbs/epos_cposmodules.h>
#include <lbs/epos_mposmodulesobserver.h>
#include "lbsdevloggermacros.h"
#include "EPos_ServerPanic.h"
#include "EPos_Global.h"
#include "EPos_CPosCallbackTimer.h"
#include "EPos_CPositionRequest.h"
#include "epos_cposmodulessettings.h"

//TODO Verify
#include "EPos_CPosLocMonitorReqHandlerHub.h"
#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "EPos_CPositionRequestTraces.h"
#endif




// CONSTANTS
#ifdef _DEBUG
_LIT(KTraceFileName, "EPos_CPositionRequest.cpp");
#endif

const TInt KParamPositionInfo = 0;

// ================= LOCAL FUNCTIONS ========================

void CancelTimerCleanup(TAny* aTimer)
    {
    (static_cast<CPosCallbackTimer*>(aTimer))->Cancel();
    }

inline TPositionInfoBase& PositionInfoBase(HBufC8* aBuffer)
    {
        return reinterpret_cast<TPositionInfoBase&>
        (const_cast<TUint8&>(*aBuffer->Ptr()));
    }
inline TPositionInfo& PositionInfo(HBufC8* aBuffer)
    {
        return reinterpret_cast<TPositionInfo&>
        (const_cast<TUint8&>(*aBuffer->Ptr()));
    }

// ================= MEMBER FUNCTIONS =======================

// C++ default constructor can NOT contain any code, that
// might leave.
//
CPositionRequest::CPositionRequest(
    CPosModuleSettings& aModuleSettings,
    CPosLocMonitorReqHandlerHub& aLocMonitorReqHandlerHub,
    TProxyPositionerConstructParams& aPositionerParams,
    TBool aIsProxy)
:
    CActive(EPriorityStandard),
    iRequestPhase(EPosReqInactive),
    iPositionerParams(aPositionerParams),
    iHasProxyPositioner(aIsProxy),
    iLocMonitorReqHandler(aLocMonitorReqHandlerHub),
    iModuleSettings(aModuleSettings)
    {
    CActiveScheduler::Add(this);
    }

// EPOC default constructor can leave.
void CPositionRequest::ConstructL()
    {
    TCallBack timeoutCallBack(HandleTimeOut, this);
    iTimeoutTimer = CPosCallbackTimer::NewL(timeoutCallBack);

    TCallBack trackingCallBack(TrackingCallback, this);
    iTrackingTimer = CPosCallbackTimer::NewL(trackingCallBack);

    iModuleSettings.PosModules().GetModuleInfoL(
        iPositionerParams.iImplementationUid, 
        iModuleInfo);
        
    if (!iModuleInfo.IsAvailable())
        {
        User::Leave(KErrNotFound);
        }

    LoadPositionerL();
    }

/**
 * Two-phased constructor.
 *
 * @param aModules Location Settings reference
 * @param aLocMonitorReqHandlerHub The hub for requests to the location monitor.
 * @param aPositionerParams contruction parameters needed when creating
 *        positioner.
 * @param aIsProxy ETrue if aImplementationUid represents a proxy PSY,
 *        EFalse otherwise.
 */
CPositionRequest* CPositionRequest::NewL(
    CPosModuleSettings& aModuleSettings,
    CPosLocMonitorReqHandlerHub& aLocMonitorReqHandlerHub,
    TProxyPositionerConstructParams& aPositionerParams,
    TBool aIsProxy)
    {
    CPositionRequest* self = new (ELeave) CPositionRequest(
        aModuleSettings,
        aLocMonitorReqHandlerHub,
        aPositionerParams,
        aIsProxy);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

/**
 * Destructor.
 */
CPositionRequest::~CPositionRequest()
    {
    // Panic client if request is outstanding
    if (IsActive())
        {
        iMessage.Panic(KPosClientFault, EPositionRequestsNotCancelled);
        }
    Cancel();
    if (iTrackingState == EPosTracking)
        {
        StopTracking();
        }
    delete iTrackingTimer;
    delete iPositionBuffer;
    delete iTimeoutTimer;
    delete iPositioner;
    }

/**
 * Starts a position request cycle. Should only be called when PSY is 
 * enabled.
 * @param aMessage the request message from the client
 */
void CPositionRequest::MakeRequestL(const RMessage2& aMessage)
    {
    if (!iModuleInfo.IsAvailable())
        {
        User::Leave(KErrNotFound);
        }

    __ASSERT_DEBUG(iPositioner, DebugPanic(EPosServerPanicPositionerNotInitialized));

    iMessage = aMessage; // Store parameter here in case of leave.

    // Clear previous position data
    delete iPositionBuffer;
    iPositionBuffer = NULL;

    HBufC8* clientBuf = Global::CopyClientBuffer8LC(aMessage, KParamPositionInfo);
    CleanupStack::Pop(clientBuf);
    iPositionBuffer = clientBuf;

    TPositionInfoBase& infoBase = PositionInfoBase(iPositionBuffer);
    TUint32 classType = infoBase.PositionClassType();

    TUint32 classesSupported = iModuleInfo.ClassesSupported(EPositionInfoFamily);

    // Check that classtype is supported and is of type TPositionInfo
    if ((classType != (classType & classesSupported)) ||
        !(classType & EPositionInfoClass))
        {
        User::Leave(KErrArgument);
        }

    // Set ModuleId to KNullId to be able to verify that Id is set by PSY.
    infoBase.SetModuleId(KNullUid);

    CleanupStack::PushL(TCleanupItem(CancelTimerCleanup, iTimeoutTimer));

    // Start timer if necessary
    if (iTimeOut.Int64() > 0)
        {
        LBS_RDEBUG_INFO("CPositionRequest::MakeRequestL() Start Timeout Timer");
        iTimeoutTimer->StartTimer(iTimeOut);
        }

    LBS_RDEBUG_VAR_INT("CPositionRequest::MakeRequestL() iTrackingState", iTrackingState);
    switch (iTrackingState)
        {
        case EPosNoTracking:
        case EPosFirstTrackingRequest:
            StartPositionDataRequestPhase();
            break;

        case EPosTracking:
            StartTrackingTimerWaitPhase();
            break;

        case EPosStopTracking:
            // This must have been handled by Cancel or RunL
        default:
            DebugPanic(EPosServerPanicTrackingInconsistency);
        }

    CleanupStack::Pop(iTimeoutTimer);
    }

/**
 * Set the TPositionUpdateOptions object.
 *
 * When this class is constructed, the TPositionUpdateOptions object
 * is constructed using default constructor with no parameters.
 *
 * @param aOptions The update options from the client.
 */
void CPositionRequest::SetUpdateOptions(
    const TPositionUpdateOptionsBase& aOptions)
    {
    iTimeOut = aOptions.UpdateTimeOut();
    TTimeIntervalMicroSeconds newInterval = aOptions.UpdateInterval();
    TTimeIntervalMicroSeconds oldInterval = iTrackingUpdateInterval;

    if (newInterval != iTrackingUpdateInterval)
        {
        iTrackingUpdateInterval = newInterval;

        if (newInterval == 0) // "stop periodic updates"
            {
            switch (iTrackingState)
                {
                case EPosFirstTrackingRequest:
                    iTrackingState = EPosNoTracking;
                    break;

                case EPosTracking:
                    if (!IsActive())
                        {
                        // can stop it right now
                        StopTracking();
                        }
                    else
                        {
                        // mark to stop later
                        iTrackingState = EPosStopTracking;
                        }
                    break;

                case EPosNoTracking:
                case EPosStopTracking:
                    break;
                default:
                    DebugPanic(EPosServerPanicTrackingInconsistency);
                    break;
                }
            }
        else if (oldInterval != 0) // "use another update interval"
            {
            if (iRequestPhase == EPosReqInactive)
                {
                TInt err;
                TRAP(err, RestartTrackingL());
                }
            else
                {
                // can't affect outstanding request
                // postpone until request is completed
                // it will be handled by RunL or DoCancel
                // via HandleTrackingStateL
                iNewTrackingInterval = ETrue;
                }
            }
        else
            {
            // oldInterval == 0
            // newInterval != 0
            // it means - "start periodic updates"
            iTrackingState = EPosFirstTrackingRequest;
            }
        }
    }

/**
 * Get the TPositionUpdateOptions object.
 * @param aOptions The TPositionUpdateOptions object.
 */
void CPositionRequest::GetUpdateOptions(
    TPositionUpdateOptionsBase& aOptions) const
    {
    aOptions.SetUpdateTimeOut(iTimeOut);
    aOptions.SetUpdateInterval(iTrackingUpdateInterval);
    }

/**
 * Stops current tracking session
 */
void CPositionRequest::NewTrackingSessionIfTracking()
    {
    /* Requestor has been changed. Call Privacy Server. */
    }

/**
 * Called when changes in locations settings occur.
 * @param aEvent Event information
 */
void CPositionRequest::HandleSettingsChangeL(TPosModulesEvent aEvent)
    {
    if (aEvent.iModuleId != iModuleInfo.ModuleId())
        {
        return;
        }

    switch (aEvent.iType)
        {
        case EPosModulesEventAvailabilityChanged:
        case EPosModulesEventModuleInstalled:
            iModuleSettings.PosModules().GetModuleInfoL(
                iModuleInfo.ModuleId(), 
                iModuleInfo);
            break;
        case EPosModulesEventModuleRemoved:
            iModuleInfo.SetIsAvailable(EFalse);
            break;
        default:
            return;
        }

    if (!iModuleInfo.IsAvailable())
        {
        if (IsActive())
            {
            CompleteClient(KErrNotFound);
            Cancel();
            }

        // Unuse positioner and unload it
        if (iTrackingState == EPosTracking)
            {
            StopPsyTracking();
            }
        delete iPositioner;
        iPositioner = NULL;
        }
    else if (!iPositioner)
        {
        // psy is re-enabled after being disabled
        LoadPositionerL();
        if (iTrackingState == EPosTracking)
            {
            StartPsyTrackingL();
            }
        }
    else
        {
        // shouldn't happen, but if it does, ignore it
        }
    }

/**
 * Called when the server class is shutting down.
 */
void CPositionRequest::NotifyServerShutdown()
    {
    if (IsActive())
        {
        DEBUG_TRACE("CPositionRequest::NotifyServerShutdown() with active request", __LINE__)
        CompleteClient(KErrServerTerminated);
        Cancel();
        }

    if (iTrackingState == EPosTracking)
        {
        StopTracking();
        }
    delete iPositioner;
    iPositioner = NULL;
    }

/**
 * From CActive
 */
void CPositionRequest::RunL()
    {
    LBS_RDEBUG_VAR_INT("CPositionRequest::RunL() iRequestPhase", iRequestPhase);
    TInt err = iStatus.Int();
    switch (iRequestPhase)
        {
        case EPosReqPositionRequest:
            {
            LBS_RDEBUG_INFO("CPositionRequest::RunL() EPosReqPositionRequest");
            // Position request finished. Cancel timer.
            iTimeoutTimer->Cancel();
            iRequestPhase = EPosReqInactive;

            CompleteRequest(err);

            HandleTrackingStateL(); // don't care if it leaves
            break;
            }

        case EPosWaitForTracking:
            StartPositionDataRequestPhase();
            break;

        default :
        	DEBUG_TRACE("CPositionRequest::RunL() panicing", __LINE__)
            DebugPanic(EPosServerPanicRequestInconsistency);
        }
    }

/**
 * From CActive
 */
TInt CPositionRequest::RunError(TInt /*aError*/)
    {
    // Happens only if HandleTrackingStateL leaves
    // which in turn means that StartTrackingL leaved somewhere
    // As request is already completed, just ignore the error
    return KErrNone;
    }

/**
 * From CActive
 */
void CPositionRequest::DoCancel()
    {
    OstTraceFunctionEntry1( CPOSITIONREQUEST_DOCANCEL_ENTRY, this );
    LBS_RDEBUG_VAR_INT("CPositionRequest::DoCancel() iRequestPhase", iRequestPhase);
    iTimeoutTimer->Cancel();

    switch (iRequestPhase)
        {
        case EPosReqPositionRequest:
            {
            __ASSERT_DEBUG(iPositioner, DebugPanic(EPosServerPanicPositionerNotInitialized));
            DEBUG_TRACE("calling CPositioner::CancelNotifyPositionUpdate()", __LINE__)
            if(iRequestTimedOut)
            	{
            	iPositioner->CancelNotifyPositionUpdate(KErrTimedOut);
            	}
            else
            	{
            	iPositioner->CancelNotifyPositionUpdate();
            	}
            break;
            }
        case EPosWaitForTracking:
            CompleteSelf(KErrCancel);
            break;

        default:
        	DEBUG_TRACE("CPositionRequest::DoCancel() panicing", __LINE__)
            DebugPanic(EPosServerPanicRequestInconsistency);
        }

    TInt err;
    if (iRequestTimedOut)
        {
        iRequestTimedOut = EFalse;
        CompleteClient(KErrTimedOut);
        TRAP(err, HandleTrackingStateL());
        }
    else
        {
        CompleteClient(KErrCancel);

        // Handle Tracking State
        if (iTrackingState == EPosStopTracking)
            {
            StopTracking();
            }
        }

    iRequestPhase = EPosReqInactive;
    OstTraceFunctionExit1( CPOSITIONREQUEST_DOCANCEL_EXIT, this );
    }


void CPositionRequest::CompleteSelf(TInt aReason)
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, aReason);
    }

void CPositionRequest::CompleteClient(TInt aReason)
    {
    if (!iMessage.IsNull())
        {
		LBS_RDEBUG_ARGINT("LBS","Client", "RunL", aReason);
        iMessage.Complete(aReason);
        }
    }

void CPositionRequest::CompleteRequest(TInt aReason)
    {
    // Return fix to the client
    if (aReason == KErrNone || aReason == KPositionPartialUpdate)
        {
        TInt err = PackPositionData();
        // err - client cannot receive result data
        CompleteClient((err != KErrNone) ? err : aReason);

        // Save current fix to LastKnownPosition handler
        // partial updates are not stored
        if ( aReason == KErrNone )
            {
            SaveAsLastKnownPosition();
            }
        }
    else
        {
        CompleteClient(aReason);
        }
    }

void CPositionRequest::StartPositionDataRequestPhase()
    {  
    LBS_RDEBUG_VAR_INT("CPositionRequest::StartPositionDataRequestPhase() iRequestPhase", iRequestPhase);
    
    __ASSERT_DEBUG(iPositioner,
        DebugPanic(EPosServerPanicPositionerNotInitialized));

    iReqStartTime.UniversalTime();

    TPositionInfo& info = PositionInfo(iPositionBuffer);

    // Set datum type to WGS84
    TPosition position;
    info.GetPosition(position);
    position.SetDatum(KPositionDatumWgs84);
    info.SetPosition(position);

    TPositionInfoBase& infoBase = reinterpret_cast<TPositionInfoBase&>(info);

    // issue request to psy
    DEBUG_TRACE("calling CPositioner::NotifyPositionUpdate()", __LINE__)

    iStatus = KRequestPending;
    iPositioner->NotifyPositionUpdate(infoBase, iStatus);

    iRequestPhase = EPosReqPositionRequest;
    SetActive();
    }

void CPositionRequest::StartTrackingTimerWaitPhase()
{
	LBS_RDEBUG_VAR_INT("CPositionRequest::StartTrackingTimerWaitPhase() iRequestPhase", iRequestPhase);
    iRequestPhase = EPosWaitForTracking;
    iStatus = KRequestPending;
    SetActive();
}

TInt CPositionRequest::PackPositionData()
    {
    TPositionInfo& info = PositionInfo(iPositionBuffer);

    // Verify that ModuleId, set by PSY, is correct if using specific PSY.
    // If a proxy PSY is used, the proxy is responsible for verifying UID.
    if (!iHasProxyPositioner &&
        info.ModuleId() != iPositionerParams.iImplementationUid)
        {
        return KErrGeneral;
        }

    // Check that datum type still is WGS84
    TPosition position;
    info.GetPosition(position);
    if (position.Datum() != KPositionDatumWgs84)
        {
        return KErrNotSupported;
        }

    TPtr8 ptrToBuffer = iPositionBuffer->Des();
    return Global::Write(iMessage, KParamPositionInfo, ptrToBuffer);
    }

void CPositionRequest::SaveAsLastKnownPosition()
    {
    TPosition pos;
    TPositionInfo& positionInfo = PositionInfo(iPositionBuffer);
    positionInfo.GetPosition(pos);

    // Don't set last known position if the position request is in the past. //TODO check if this is required
    if (iReqStartTime <= pos.Time())
        {
        iLocMonitorReqHandler.SetPositionInfo(positionInfo);
        }
    }

TInt CPositionRequest::HandleTimeOut(TAny* aPositionRequest)
    {
    CPositionRequest* self =
        reinterpret_cast<CPositionRequest*>(aPositionRequest);
    
    LBS_RDEBUG_VAR_INT("CPositionRequest::HandleTimeOut() iRequestPhase", self->iRequestPhase);
    DEBUG_TRACE("CPositionRequest::HandleTimeOut()", __LINE__)    
    __ASSERT_DEBUG(self->iRequestPhase == EPosReqPositionRequest || self->iRequestPhase == EPosWaitForTracking,
        DebugPanic(EPosServerPanicRequestInconsistency));

    self->iRequestTimedOut = ETrue;
    self->Cancel();

    return KErrNone;
    }

TInt CPositionRequest::TrackingCallback(TAny* aPositionRequest)
    {
    CPositionRequest* self =
        reinterpret_cast<CPositionRequest*>(aPositionRequest);

    // continue tracking timer
    if (self->iTrackingState == EPosTracking)
        {
        self->ContinueTrackingTimer();
        }

    if (self->iRequestPhase == EPosWaitForTracking)
        {
        // This is the normal case. The client's request was delayed
        // by the update interval.
        // Complete tracking timer waiting and go next stage
        self->CompleteSelf(KErrNone);
        }

    return KErrNone;
    }

void CPositionRequest::HandleTrackingStateL()
    {
    switch (iTrackingState)
        {
        case EPosNoTracking:
            break;

        case EPosFirstTrackingRequest:
            // start internal tracking
            iTrackingState = EPosTracking;
            ContinueTrackingTimer();

            StartPsyTrackingL();
            break;

        case EPosTracking:
            // if tracking interval was changed
            if (iNewTrackingInterval)
                {
                RestartTrackingL();
                }
            break;

        case EPosStopTracking:
            StopTracking();
            break;

        default:
            DebugPanic(EPosServerPanicTrackingInconsistency);
        }
    }

void CPositionRequest::StartPsyTrackingL()
    {
    __ASSERT_DEBUG(iPositioner, DebugPanic(EPosServerPanicPositionerNotInitialized));

    iNewTrackingInterval = EFalse;

    DEBUG_TRACE("calling CPositioner::TrackingOverridden()", __LINE__)
    if (iPositioner->TrackingOverridden())
        {
        DEBUG_TRACE("calling CPositioner::StartTrackingL()", __LINE__)
        iPositioner->StartTrackingL(iTrackingUpdateInterval);
        iPositionerTrackingStarted = ETrue;
        }
    }

void CPositionRequest::RestartTrackingL()
    {
    LBS_RDEBUG_INFO("CPositionRequest::RestartTrackingL()");
    iTrackingTimer->Cancel();
    StopPsyTracking();
    ContinueTrackingTimer();
    StartPsyTrackingL();
    }

void CPositionRequest::StopTracking()
    {
    LBS_RDEBUG_INFO("CPositionRequest::StopTracking()");
    iTrackingTimer->Cancel();
    iTrackingState = EPosNoTracking;

    StopPsyTracking();
    }

void CPositionRequest::StopPsyTracking()
    {
    if (iPositioner && iPositionerTrackingStarted)
        {
        DEBUG_TRACE("calling CPositioner::StopTracking()", __LINE__)
        iPositioner->StopTracking();
        iPositionerTrackingStarted = EFalse;
        }
    }

void CPositionRequest::ContinueTrackingTimer()
    {
    LBS_RDEBUG_INFO("CPositionRequest::ContinueTrackingTimer()");
    iTrackingTimer->StartTimer(iTrackingUpdateInterval);
    }

void CPositionRequest::LoadPositionerL()
    {
    iPositioner = CPositioner::NewL(&iPositionerParams);
    iPositionerTrackingStarted = EFalse;
    }

void CPositionRequest::ExtendUpdateTimeOut(const TTimeIntervalMicroSeconds& aAdditionalTime)
	{
	LBS_RDEBUG_INFO("CPositionRequest::ExtendUpdateTimeOut()");
	iTimeoutTimer->ExtendTimeout(aAdditionalTime);
	}

//  End of File