mpx/playbackframework/playbackserver/src/mpxplaybackserver.cpp
author Simon Howkins <simonh@symbian.org>
Thu, 18 Nov 2010 13:26:55 +0000
branchRCL_3
changeset 69 71775bb0f6c0
parent 34 e257e2b6459d
permissions -rw-r--r--
Adjusted to avoid exports, etc, from a top-level bld.inf

/*
* Copyright (c) 2004 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:  Playback server
*
*/


#include <e32std.h>
#include <e32svr.h>
#ifdef RD_TSP_CLIENT_MAPPER
#include <tspclientmapper.h>
#endif
#include <mpxclientlist.h>
#include <mpxpluginmonitor.h>
#include <mpxlog.h>
#include "mpxplaybackserverdefs.h"
#include "mpxplaybackengine.h"
#include "mpxplaybackserversession.h"
#include "mpxplaybackserver.h"

// CONSTANTS

// Server Security Policy
const TUint KMPXPlaybackServerRangeCount = 2;
const TInt KMPXPlaybackServerRanges[KMPXPlaybackServerRangeCount] =
    {
    0,                  //range is [0-EPbsServerOpEnd)
    EPbsServerOpEnd,    //range is [EPbsServerOpEnd-KMaxTInt]
    };
const TUint8 KMPXPlaybackSeverElementsIndex[KMPXPlaybackServerRangeCount] =
    {
    0,                            //applies to range [0-EPbsServerOpEnd)
    CPolicyServer::ENotSupported, //applies to range [EPbsServerOpEnd-KMaxTInt]
    };

const CPolicyServer::TPolicyElement KMPXPlaybackServerPolicyElements[] =
    {
    {_INIT_SECURITY_POLICY_C1(ECapability_None),
                              CPolicyServer::EFailClient},
    };

const CPolicyServer::TPolicy KMPXPlaybackServerPolicy =
    {
    CPolicyServer::EAlwaysPass, //specifies all connect attempts should pass
    KMPXPlaybackServerRangeCount,
    KMPXPlaybackServerRanges,
    KMPXPlaybackSeverElementsIndex,
    KMPXPlaybackServerPolicyElements,
    };

// ----------------------------------------------------------------------------
// Start playback server
//
LOCAL_C void StartServerL()
    {
    User::LeaveIfError(User::RenameThread(KMPXPlaybackServerName));
    CActiveScheduler* scheduler = new(ELeave)CActiveScheduler;
    CleanupStack::PushL(scheduler);
    CActiveScheduler::Install(scheduler);
    CMPXPlaybackServer* server = CMPXPlaybackServer::NewL();
    CleanupStack::PushL(server);
    RProcess::Rendezvous(KErrNone);
    CActiveScheduler::Start();
    CleanupStack::PopAndDestroy(server);
    CActiveScheduler::Install(NULL);
    CleanupStack::PopAndDestroy(scheduler);
    }

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

// ----------------------------------------------------------------------------
// Two-phased constructor.
// ----------------------------------------------------------------------------
//
CMPXPlaybackServer* CMPXPlaybackServer::NewL()
    {
    CMPXPlaybackServer *pS = new(ELeave) CMPXPlaybackServer(
                                                CActive::EPriorityStandard,
                                                KMPXPlaybackServerPolicy);
    CleanupStack::PushL(pS);
    pS->ConstructL();
    CleanupStack::Pop(pS);
    return pS;
    }

// ----------------------------------------------------------------------------
// C++ constructor can NOT contain any code that might leave.
// ----------------------------------------------------------------------------
//
CMPXPlaybackServer::CMPXPlaybackServer(TInt aPriority, const TPolicy &aPolicy)
    : CPolicyServer(aPriority, aPolicy)
    {}

// ----------------------------------------------------------------------------
// Destructor
// ----------------------------------------------------------------------------
//
CMPXPlaybackServer::~CMPXPlaybackServer()
    {
    iPlayers.ResetAndDestroy();
    delete iPluginMonitor;
#ifdef RD_TSP_CLIENT_MAPPER
    delete iClientMapper;
#endif
    }

// ----------------------------------------------------------------------------
// Symbian 2nd phase constructor can leave.
// ----------------------------------------------------------------------------
//
void CMPXPlaybackServer::ConstructL()
    {
    StartL(KMPXPlaybackServerName);
    iPluginMonitor = CMPXPluginMonitor::NewL(KMPXPlaybackInterfaceUid);
#ifdef RD_TSP_CLIENT_MAPPER
    iClientMapper = CTspClientMapper::NewL();
#endif
    }

// ----------------------------------------------------------------------------
// Searches process id for target selector plugin.
// When the player has been opened in a mode, which is bound to the same engine
// than certain application's engine, then need to fetch its process id.
// (As a reference for parameter aMode see MMPXPlaybackUtility modes.)
// Otherwise target selector plugin is notified wrongly about client statuses 
// and key events are not propagated to correct application.
// ----------------------------------------------------------------------------
//
void CMPXPlaybackServer::FindProcessIdForTsp(
    const CMPXPlaybackEngine* aEngine,
    TProcessId& aProcessId )
    {
    TBool processFound( EFalse );
    TFindProcess processFinder;
    TFullName processName;

    while ( processFinder.Next( processName ) == KErrNone && !processFound )
        {
        RProcess process;
        TInt err = process.Open( processFinder );
        if( err == KErrNone )
            {
            if( process.SecureId().iId == aEngine->ModeId().iUid && 
                process.ExitType() == EExitPending )
                {
                MPX_DEBUG4("CMPXPlaybackServer::FindProcessIdForTsp(): pid changed from %d to %d (mode 0x%x)",
                           TUint(aProcessId), TUint(process.Id()), aEngine->ModeId().iUid);
                aProcessId = process.Id();
                processFound = ETrue;
                }
            process.Close();
            }
        }
    }

// ----------------------------------------------------------------------------
// Increments number of sessions this server holds
// ----------------------------------------------------------------------------
//
void CMPXPlaybackServer::AddClient()
    {
    iClients++;
    }


// ----------------------------------------------------------------------------
// Find the player that has client aName, and remove it from that player; if
// that player has no clients, then delete that player. If the server has no
//  more client sessions, then stop the server.
// ----------------------------------------------------------------------------
//
void CMPXPlaybackServer::RemoveClient(const CMPXMessageQueue& aMsgQueue)
    {
    MPX_DEBUG2("CMPXPlaybackServer::RemoveClient from %d",
                iPlayers.Count());
    for (TInt i=iPlayers.Count();--i>=0;)
        {
        CMPXPlaybackEngine* p=iPlayers[i];
        CMPXClientList* cl=p->ClientList();
        TInt index(cl->Find(aMsgQueue));
        if (KErrNotFound != index)
            {
            MPX_DEBUG1("CMPXPlaybackServer::RemoveClient found client");
            cl->RemoveClient(index);
            if (cl->ClientCount()==0)
                {
                MPX_DEBUG1("CMPXPlaybackServer::RemoveClient delete a player");
                CMPXPlaybackEngine* enginePtr = p; 
                delete p;
				//Due to callbacks in PlaybackServer we have to remove engine from the iPlayers array after deleting.
                //enginePtr is a invalid pointer as p is already deleted
    		    //Find the index of deleted engine using its address
				TInt engineIndex = iPlayers.Find(enginePtr);
                if ( engineIndex != KErrNotFound )
                    {
                    iPlayers.Remove(engineIndex);
                    }
                }
            break;
            }
        }
    iClients--;
    ASSERT(iClients>=0);
    if (iClients==0)
        {
        CActiveScheduler::Stop();
        }
    }

// ----------------------------------------------------------------------------
// Return last active process id
// ----------------------------------------------------------------------------
//
TProcessId CMPXPlaybackServer::LastActiveProcessId()
    {
    TProcessId id(KNullProcessId);
    if (iPlayers.Count())
        {
        id = iPlayers[0]->LastActiveProcessId();
        }
    return id;
    }

// ----------------------------------------------------------------------------
// Creates a brand new player to the player list with this client name and TID,
// and whether it's the music player.
// ----------------------------------------------------------------------------
//
CMPXPlaybackEngine* CMPXPlaybackServer::AddNewPlayerL(
    TThreadId aClientId,
    const TUid& aModeId,
    CMPXMessageQueue* aMsgQueue,
    const TInt aCategory) 
    {
    CMPXPlaybackEngine* p =
                CMPXPlaybackEngine::NewL(*iPluginMonitor,*this,this,aModeId, aCategory); 
    MPX_DEBUG2("CMPXPlaybackServer::AddNewPlayerL 0x%08x", p);
    CleanupStack::PushL(p);
    p->ClientList()->AddClientL(aClientId, aModeId.iUid, aMsgQueue, aCategory); 
    iPlayers.AppendL(p);
    CleanupStack::Pop(p);
    return p;
    }

// ----------------------------------------------------------------------------
// Create player according to the mode:
// ----------------------------------------------------------------------------
//
CMPXPlaybackEngine* CMPXPlaybackServer::CreatePlayerL(
    const TUid& aModeId,
    const TInt aCategory, 
    TThreadId aClientId,
    CMPXMessageQueue* aMsgQueue)
    {
    CMPXPlaybackEngine* p=NULL;
    if (aModeId == KPbModeDefault)
        {
        // Add the new client to the player created by another client running
        // in the same thread. If there isn't one, then create the first player
        // for this thread
        //
        p=AddToDefaultPlayerL(aClientId,aMsgQueue,aCategory);
        if(!p)
            {

            p=AddNewPlayerL(aClientId, aModeId, aMsgQueue, aCategory);
            }
        }
        //
    else if (aModeId == KPbModeNewPlayer)
        {
        //
        // Create a brand new player for this client
        //
        p=AddNewPlayerL(aClientId, aModeId, aMsgQueue, aCategory);
        }
    else if (aModeId == KPbModeActivePlayer)
        { 
        TInt index = IndexOfRecentActivePlayer(aCategory);
        if (KErrNotFound != index)
            {
            p = iPlayers[index]; // active & matching category
            }
        if (!p && iPlayers.Count()>0)
            { //no recent activeplayer, attach to the first player with matching category
            TInt count = iPlayers.Count();
            for(TInt i = 0; i < count; i++)
                {
                if(iPlayers[i]->Category() == aCategory)
                    {
                    p = iPlayers[i]; // not active, first matching category 
                    break;
                    }
                }
            }
        if (p)
            {
            p->ClientList()->AddClientL(aClientId,aModeId.iUid,aMsgQueue, aCategory);
            }
        else
            { // no player available (no active player with matching category)
            MPX_DEBUG1("CMPXPlaybackServer::CreatePlayerL create a new player");	
            p=AddNewPlayerL(aClientId, aModeId, aMsgQueue, aCategory);
            }
        }
    else
        {
        //
        // Add the new client to the player created by one application. If
        // there isn't one, then create the first player for the application
        //
        for (TInt i=iPlayers.Count();--i>=0;)
            {
            if (iPlayers[i]->ModeId() == aModeId)
                {
                p = iPlayers[i];
                p->ClientList()->AddClientL(aClientId,
                                            aModeId.iUid, aMsgQueue, aCategory);
                break;
                }
            }

        if (!p)
            {		
            p=AddNewPlayerL(aClientId, aModeId, aMsgQueue, aCategory);		
            }
        }
    return p;
    }

// ----------------------------------------------------------------------------
// Add the new client (defined by it's TID) to the player
// associated with the Tid aExistingClientId
// ----------------------------------------------------------------------------
//
CMPXPlaybackEngine* CMPXPlaybackServer::AddToDefaultPlayerL(
    TThreadId aClientId,
    CMPXMessageQueue* aMsgQueue,
    const TInt aCategory)
    {
    CMPXPlaybackEngine* p=NULL;
    for (TInt i=0; i < iPlayers.Count(); ++i)
        {
        p=iPlayers[i];
        CMPXClientList* cl=p->ClientList();
        TInt index = cl->Find(aClientId);
        if (KErrNotFound != index)
            {
            // if the player is an active player, change mode from default
            // to activeplayer. Default mode always uses the mode of first client
            TInt mode = (cl->ClientMode(index) == KPbModeActivePlayer.iUid) ?
                         KPbModeActivePlayer.iUid : KPbModeDefault.iUid;
            MPX_DEBUG3("CMPXPlaybackServer::AddToDefaultPlayerL mode 0x%08x, engine 0x%08x",
                       mode, p);				   
            cl->AddClientL(aClientId,mode,aMsgQueue, aCategory);
            break;
            }
        }
    return p;
    }

// ----------------------------------------------------------------------------
// Return the index of an active player. If there is no active player, return
// the index to the player in paused state which may be interrupted.
// ----------------------------------------------------------------------------
//
TInt CMPXPlaybackServer::IndexOfRecentActivePlayer(TInt aCategory)
    {
    TInt ret(KErrNotFound);
    for (TInt i=0; i<iPlayers.Count(); ++i)
        {
        CMPXPlaybackEngine* p = iPlayers[i];
        if (p->IsPlayerActive() && aCategory == p->Category())
            { // Found active player
            ret = i;
            break;
            }
        else if (EPbStatePaused == p->State() && KErrNotFound == ret && aCategory == p->Category())
            { // first paused player
            ret = i;
            }
        }
    return ret;
    }

TInt CMPXPlaybackServer::IndexOfRecentActivePlayer()
    {
    TInt ret(KErrNotFound);
    for (TInt i=0; i<iPlayers.Count(); ++i)
        {
        CMPXPlaybackEngine* p = iPlayers[i];
        if (p->IsPlayerActive())
            { // Found active player
            ret = i;
            break;
            }
        else if (EPbStatePaused == p->State() && KErrNotFound == ret)
            { // first paused player
            ret = i;
            }
        }
    return ret;
    }

// ----------------------------------------------------------------------------
// Handle active player changed evnet.
// Notify all clients of EPbModeActivePlayer mode in inactive engines
// ----------------------------------------------------------------------------
//
void CMPXPlaybackServer::HandleActiveEngineL(
    const CMPXPlaybackEngine* aEngine,
    TBool aActive)
    {
    MPX_DEBUG3("CMPXPlaybackServer::HandleActiveEngineL Engine: 0x%08x, Active %d",
               aEngine, aActive);
    TInt i(0);
    TInt indexOfActive = iPlayers.Find(aEngine);
    ASSERT(KErrNotFound!=indexOfActive);
    if (!aActive)
        {
#ifdef RD_TSP_CLIENT_MAPPER
        TProcessId lastPid( aEngine->LastActiveProcessId() ); 
        FindProcessIdForTsp( aEngine, lastPid );
        iClientMapper->SetTspTargetClientToOtherType(
                CTspClientMapper::EStoppedClients,
                lastPid);
        MPX_DEBUG2("CMPXPlaybackServer::HandleActiveEngineL(): Added as stopped client %d",
                   TUint( lastPid));
#endif
        if (EPbStatePaused != aEngine->State())
            {
            indexOfActive = IndexOfRecentActivePlayer();
            }
        else
            { // else no need to rebind. if the player is interruptted by other player
              // there must be another HandleActiveEngineL callback with aActive = ETrue
            indexOfActive = KErrNotFound;
            }
        }
    else
        {// else aEngine is active player
#ifdef RD_TSP_CLIENT_MAPPER
        TProcessId lastPid( aEngine->LastActiveProcessId() ); 
        FindProcessIdForTsp( aEngine, lastPid );
        TInt err = iClientMapper->SetTspTargetClientToOtherType( 
            CTspClientMapper::EPlayingClients, lastPid );
        if ( err != KErrNone && (TUint)lastPid != KNullProcessId )
            {
            // Setting target type failed probably because client PID could not be found.
            // As a fallback set client as new playing client.
            MPX_DEBUG2("CMPXPlaybackServer::HandleActiveEngineL(): Adding to playing client %d", 
                (TUint)lastPid );
            iClientMapper->SetTspTargetClient( CTspClientMapper::EPlayingClients, lastPid );
            }
        MPX_DEBUG2("CMPXPlaybackServer::HandleActiveEngineL(): Added as playing client %d", 
            (TUint)lastPid );
#endif
        }

    if (KErrNotFound != indexOfActive)
        {
        // Move the active player engine to the top of player list
        CMPXPlaybackEngine* player(NULL);
        if (indexOfActive)
            { // not 0
            player = iPlayers[indexOfActive];
            iPlayers.Remove(indexOfActive);
            iPlayers.Insert(player, 0);
            }

        // Active engine changed, notify all clients who are instrested.
        // e.g. KPbModeActivePlayer
        for (i=1; i < iPlayers.Count(); ++i)
            {
            player = iPlayers[i];
            if (player == aEngine && aActive)
                {
                continue;
                }
            MPX_DEBUG2("CMPXPlaybackServer::HandleActiveEngineL Player: 0x%08x", player);
            CMPXClientList* cl = player->ClientList();
            for (TInt j = cl->ClientCount(); --j>=0;)
                {
                if ((KPbModeActivePlayer.iUid == cl->ClientMode(j)) && 
                        (aEngine->Category() == cl->ClientCategory(j))) 
                    {
                    MPX_DEBUG2(
                      "CMPXPlaybackServer notify %d client player changed", j);
                    player->ClientList()->SendMsgL(
                                j,
                                TMPXPlaybackMessage(
                                    TMPXPlaybackMessage::EActivePlayerChanged,
                                    1, aActive));
                    }
                }
            }
        }
    }

// ----------------------------------------------------------------------------
// Observe client change event
// ----------------------------------------------------------------------------
//
void CMPXPlaybackServer::HandleClientChange(
    const TProcessId& aPid,
    MMPXClientlistObserver::TChangeType aChangeType)
    {
#ifdef RD_TSP_CLIENT_MAPPER
    if (MMPXClientlistObserver::EAdd == aChangeType)
        {
        iClientMapper->SetTspTargetClient(
                CTspClientMapper::ERegisteredClients,
                aPid);
        MPX_DEBUG2("CMPXPlaybackServer::HandleClientChange(): Added as registered client %d",
                   TUint( aPid ));
        }
    else
        {
        iClientMapper->RemoveTspTargetClient(
                CTspClientMapper::EPlayingClients,
                aPid);
        MPX_DEBUG2("CMPXPlaybackServer::HandleClientChange(): Removed from EPlayingClients client %d",
                   TUint( aPid ));
        }
#else
    (void)aPid;
    (void)aChangeType;
#endif
    }

// ----------------------------------------------------------------------------
// Create a new session
// ----------------------------------------------------------------------------
//
CSession2* CMPXPlaybackServer::NewSessionL(const TVersion& aVersion,
                                           const RMessage2& /*aMessage*/) const
    {
    TVersion v(KMPXPlaybackServerMajorVersionNumber,
               KMPXPlaybackServerMinorVersionNumber,
               KMPXPlaybackServerBuildVersionNumber);
    if (!User::QueryVersionSupported(v,aVersion))
        User::Leave(KErrNotSupported);
    CMPXPlaybackSession* session = CMPXPlaybackSession::NewL();
    const_cast<CMPXPlaybackServer*>(this)->AddClient();
    return session;
    }

// ----------------------------------------------------------------------------
// Server exe entry
// ----------------------------------------------------------------------------
//
TInt E32Main()
    {
    __UHEAP_MARK;
    //
    CTrapCleanup* cleanup=CTrapCleanup::New();
    TInt r=KErrNoMemory;
    if (cleanup)
        {
        TRAP(r,StartServerL());
        }

    delete cleanup;
    __UHEAP_MARKEND;
    return r;
    }

// End of file