usbengines/usbwatcher/src/cusbactivepersonalityhandler.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 15:41:46 +0300
branchRCL_3
changeset 92 dde4619868dc
parent 80 e02eb84a14d2
permissions -rw-r--r--
Revision: 201038 Kit: 201041

/*
* Copyright (c) 2002-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 implements UsbActivePersonalityHandler class.
*
*/


// INCLUDE FILES
#include <etelmm.h>             // for fetching the IMEI code
#include <mmtsy_names.h>        // for RTelServer names
#include <UsbWatcherInternalCRKeys.h>
#include <cusbpersonalitynotifier.h>
#include <cusbpersonalityplugin.h>
#include <tusbpersonalityparams.h>
#include <startupdomainpskeys.h> //for global system state
#include "cusbactivepersonalityhandler.h"
#include "cusbglobalsystemstateobserver.h"
#include <usbuinotif.h>

// CONSTANTS
const TInt KSerialNumberLength = 12;

const TUid KUsbmanSvrUid = {0x101fe1db};

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

// ----------------------------------------------------------------------------
// C++ default constructor can NOT contain any code, that might leave.
// ----------------------------------------------------------------------------
//
CUsbActivePersonalityHandler::CUsbActivePersonalityHandler(
    RUsb& aUsbMan, CUsbWatcher& aOwner )
    : CActive( EPriorityStandard )
    , iUsbMan( aUsbMan )
    , iOwner( aOwner )
    , isFailureCleanup( EFalse )
    {
    CActiveScheduler::Add( this );
    }

// ----------------------------------------------------------------------------
// Symbian 2nd phase constructor can leave.
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::ConstructL()
    {
    LOG_FUNC

    iPersonalityNotifier = CUsbPersonalityNotifier::NewL();
    iPersonalityParams = new ( ELeave ) TUsbPersonalityParams( iUsbMan,
            *iPersonalityNotifier );

    ConstructUsbSerialNumberL();
    }

// ----------------------------------------------------------------------------
// Two-phased constructor.
// ----------------------------------------------------------------------------
//
CUsbActivePersonalityHandler* CUsbActivePersonalityHandler::NewL(
        RUsb& aUsbMan, CUsbWatcher& aOwner )
    {
    LOG_FUNC

    CUsbActivePersonalityHandler* self = new ( ELeave )
            CUsbActivePersonalityHandler( aUsbMan, aOwner );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop(); // pop self
    return self;
    }

// Destructor
CUsbActivePersonalityHandler::~CUsbActivePersonalityHandler()
    {
    LOG_FUNC

    Cancel(); // cancel any outstanding requests

    delete iCurrentPersonalityHandler;
    delete iPersonalityNotifier;
    delete iPersonalityParams;
    }

// ----------------------------------------------------------------------------
// Construct USB serial number. Some of the personalities may need this.
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::ConstructUsbSerialNumberL()
    {
    LOG_FUNC

    RTelServer telServer;
    RMobilePhone phone;

    LEAVEIFERROR( telServer.Connect() );
    CleanupClosePushL( telServer );

    LEAVEIFERROR( telServer.LoadPhoneModule( KMmTsyModuleName ) );

    LEAVEIFERROR( phone.Open( telServer, KMmTsyPhoneName ) );
    CleanupClosePushL( phone );

    // to store the serial number to be published as the USB device's serial
    // number, fetch it from telephony server.
    TRequestStatus status;
    phone.GetPhoneId( status, iPhoneInfo );
    User::WaitForRequest( status );

    // make sure that the serial number fulfills USB requirements and then
    // convert the serial number so that it can be printed to log
    TLex lex( iPhoneInfo.iSerialNumber );
    TInt length = iPhoneInfo.iSerialNumber.Length();

    if( length < KSerialNumberLength )
        {
        // In GSM, the complete IMEI can be used as USB serial
        // number. But in CDMA, the ESN is too short for a valid Mass
        // Storage serial number (Mass-Storage and Bulk Only Transport
        // specs both require minimum 12 byte number), so it is
        // extended with trailing zeroes. When doing this, make sure
        // not to write anything over descriptor's max length
        if( iPhoneInfo.iSerialNumber.MaxLength() < KSerialNumberLength )
            {
            iPhoneInfo.iSerialNumber.SetLength( KSerialNumberLength );
            }
        while( length < KSerialNumberLength )
            {
            iPhoneInfo.iSerialNumber.Append( '0' );
            ++length;
            }
        }

    CleanupStack::PopAndDestroy( 2, &telServer );
    }

// ----------------------------------------------------------------------------
// Confirm that personality can be loaded.
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::ConfirmPersonalityUnload( TRequestStatus&
        aStatus )
    {
    LOG_FUNC

    aStatus = KRequestPending;
    iRequestStatus = &aStatus;

    if( iState != EUsbPersonalityStarted )
        {
        TRequestStatus* status = &iStatus;
        SetActive();
        User::RequestComplete( status, KErrGeneral );
        return;
        }

    // Cancel all notes before confirmation
    iPersonalityParams->PersonalityNotifier().CancelAll();

    if( iCurrentPersonalityHandler )
        {
        iCurrentPersonalityHandler->ConfirmPersonalityUnload( iStatus );
        SetActive();
        }
    else
        {
        TRequestStatus* status = &iStatus;
        SetActive();
        User::RequestComplete( status, KErrNone );
        }
    }

// ----------------------------------------------------------------------------
// Start personality.
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::StartPersonality( TInt& aPersonalityId,
        TInt aAskOnConnectionSetting, TRequestStatus& aStatus )
    {
    LOG_FUNC

    LOG2( "PersonalityId = %d, AskOnConnectionSetting = %d", aPersonalityId,
            aAskOnConnectionSetting );

    // Remove all notes.
    iPersonalityNotifier->CancelAll();
     

    iPersonalityId = &aPersonalityId;
    iAskOnConnectionSetting = aAskOnConnectionSetting;
    aStatus = KRequestPending;
    iRequestStatus = &aStatus;

    // prepare current personality for start and return
    if( iCurrentPersonalityHandler )
        {
        LOG( "Previous PersonalityHandler not deleted" );
        User::RequestComplete( iRequestStatus, KErrGeneral );
        return;
        }

    TRAPD( ret, ( iCurrentPersonalityHandler = NewPersonalityHandlerL(
            *iPersonalityId ) ) );

    if( ( ret == KErrNone) && iCurrentPersonalityHandler )
        {
        LOG( "PersonalityHandler created" );
        iCurrentPersonalityHandler->PreparePersonalityStart( iStatus );
        iState = EUsbPersonalityPrepareStart;
        isFailureCleanup = EFalse;
        SetActive();
        }
    else
        {
        LOG( "Error: PersonalityHandler create failed" );
        User::RequestComplete( iRequestStatus, KErrGeneral );
        }
    }

// ----------------------------------------------------------------------------
// Stop current personality.
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::StopPersonality( TRequestStatus& aStatus )
    {
    LOG_FUNC

    aStatus = KRequestPending;
    iRequestStatus = &aStatus;

    iState = EUsbPersonalityPrepareStop;
    isFailureCleanup = EFalse;
    
    // prepare current personality for stop and return
    if( iCurrentPersonalityHandler )
        {
        LOG( "Call PersonalityPlugin to prepare stop" );
        iCurrentPersonalityHandler->PreparePersonalityStop( iStatus );
        SetActive();
        }
    else
        {
        LOG( "No current PersonalityPlugin, return immediately" );
        TRequestStatus* status = &iStatus;
        SetActive();
        User::RequestComplete( status, KErrNone );
        }
    }

// ----------------------------------------------------------------------------
// Indicates USB device state change to personality when USB is started.
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::StateChangeNotify(
        TUsbDeviceState aStateOld, TUsbDeviceState aStateNew )
    {
    LOG_FUNC

    iDeviceState = aStateNew;        
    switch ( aStateNew )
        {
        case EUsbDeviceStateAddress:
            {
            if( iAskOnConnectionSetting &&
                    ( aStateOld != EUsbDeviceStateSuspended ) &&
                    ( aStateOld != EUsbDeviceStateConfigured ) 
                     )
                {
                iPersonalityParams->PersonalityNotifier().ShowQuery(
                        KCableConnectedNotifierUid, iDummy,
                        iPersonalityPckgBuf);
                }
            break;
            }
		case EUsbDeviceStateUndefined:
            {
            iPersonalityNotifier->CancelQuery(KQueriesNotifier);
            break;
			}
       default:
            // We do not handle other state here
			LOG( "DeviceStatechange ignored by ActivePersonalityhandler or EUsbDeviceStateConfigured" );
            break;
        }

    if( iCurrentPersonalityHandler )
        {
        LOG( "Notifying PersonalityPlugin of the new state" );
        iCurrentPersonalityHandler->StateChangeNotify( aStateNew );
        }
    }

// ----------------------------------------------------------------------------
// Standard active object error function. Handle error and complete
// outstanding request. We must not come here.
// ----------------------------------------------------------------------------
//
TInt CUsbActivePersonalityHandler::RunError( TInt aError )
    {
    LOG_FUNC
    
    LOG2("Returned error: %d, iState: %d", aError, iState);

    switch ( aError )
        {
        case KErrNoMemory:
            iQueryParams().iQuery = EUSBNotEnoughRam;
            iPersonalityParams->PersonalityNotifier().ShowQuery(KQueriesNotifier, 
    	                iQueryParams, iDummyBuf);
            break;
        case KErrDiskFull:
            iQueryParams().iQuery = EUSBDiskFull;
            iPersonalityParams->PersonalityNotifier().ShowQuery(KQueriesNotifier, 
    	                iQueryParams, iDummyBuf);
            break;
        default:
        	LOG( "Ignored" );
        }

    //only handle error when TryStart fails now
	//clean up work to be done in the personality
    if (iState == EUsbPersonalityStartUsb)
        {
        iState = EUsbPersonalityPrepareStop;
		isFailureCleanup = ETrue;
		
	    // prepare current personality for stop and return
	    if( iCurrentPersonalityHandler )
	        {
	        LOG( "Call PersonalityPlugin to prepare stop" );
	        iCurrentPersonalityHandler->PreparePersonalityStop( iStatus );
	        SetActive();
	        }
	    else
	    	{    
            LOG( "No current PersonalityPlugin" );
            }

        //complete StartPersonality with aError
        User::RequestComplete( iRequestStatus, aError );
        }
    else
        {
	    LOG( "Ignore error in other states" );
        }

    return KErrNone;
    }

// ----------------------------------------------------------------------------
// Executed when iStatus is completed.
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::RunL()
    {
    LOG_FUNC

    TInt ret = iStatus.Int();
    
    LOG2( "CUsbActivePersonalityHandler::RunL iStatus = %d, iState = %d", ret, iState );

    switch( iState )
        {
        case EUsbPersonalityIdle:
            break;

        case EUsbPersonalityPrepareStart:
            {
            LOG( "EUsbPersonalityPrepareStart" );
            // then write the serial number to P&S for USB Manager
            PublishSerialNumber();
            iUsbMan.TryStart( *iPersonalityId, iStatus );
            iState = EUsbPersonalityStartUsb;
            SetActive();
            }
            break;

        case EUsbPersonalityStartUsb:
            LEAVEIFERROR( ret );
            LOG( "EUsbPersonalityStartUsb" );
            iState = EUsbPersonalityFinishStart;
            if( iCurrentPersonalityHandler )
                {
                iCurrentPersonalityHandler->FinishPersonalityStart( iStatus );
                SetActive();
                }
            else
                {
                TRequestStatus* status = &iStatus;
                SetActive();
                User::RequestComplete( status, KErrNone );
                }
            break;

        case EUsbPersonalityFinishStart:
            LOG( "EUsbPersonalityFinishStart" );
            User::RequestComplete( iRequestStatus, ret );
            iState = EUsbPersonalityStarted;
            break;

        case EUsbPersonalityPrepareStop:
            LOG( "EUsbPersonalityPrepareStop" );
            iUsbMan.TryStop( iStatus );
            iState = EUsbPersonalityStopUsb;
            SetActive();
            break;

        case EUsbPersonalityStopUsb:
            LOG( "EUsbPersonalityStopUsb" );
            iState = EUsbPersonalityFinishStop;
            if( iCurrentPersonalityHandler )
                {
                iCurrentPersonalityHandler->FinishPersonalityStop( iStatus );
                SetActive();
                }
            else
                {
                TRequestStatus* status = &iStatus;
                SetActive();
                User::RequestComplete( status, KErrNone );
                }
            break;

        case EUsbPersonalityFinishStop:
            LOG( "EUsbPersonalityFinishStop" );

            delete iCurrentPersonalityHandler;
            iCurrentPersonalityHandler = NULL;

            //iAskOnConnectionSetting may be have been set to off
            if ( iDeviceState == EUsbDeviceStateUndefined )
                {
	            iPersonalityParams->PersonalityNotifier().CancelQuery(
	                    KCableConnectedNotifierUid );
                }
            //the request should be completed with error code in RunError if failed
            if ( !isFailureCleanup )
                {
                User::RequestComplete( iRequestStatus, ret );
                }
            
            iState = EUsbPersonalityIdle;
            break;

        case EUsbPersonalityStarted:
            // This must unload event. Let's complete.
            User::RequestComplete( iRequestStatus, ret );
            break;

        default:
            LOG( "ERROR: unexpected state" );
            PANIC( KErrGeneral );
            break;
        }
    }

// ----------------------------------------------------------------------------
// Standard active object cancellation function.
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::DoCancel()
    {
    LOG_FUNC

    LOG1( "CUsbActivePersonalityHandler::iState = %d", iState );
    switch( iState )
        {
        case EUsbPersonalityFinishStart:
            {
            TRequestStatus status;

            iUsbMan.TryStop( status );
            SetActive();
            User::WaitForRequest( status );
            }
            // Don't break. We need to cancel outstanding request.

        case EUsbPersonalityStarted:
        case EUsbPersonalityPrepareStop:
        case EUsbPersonalityFinishStop:
            if( iCurrentPersonalityHandler )
                {
                iCurrentPersonalityHandler->Cancel();
                }
            break;

        case EUsbPersonalityStopUsb:
            iUsbMan.CancelInterest( RUsb::ETryStop );
            break;

        case EUsbPersonalityStartUsb:
            iUsbMan.StartCancel();
            break;

        default:
            break;
        }

    if( iCurrentPersonalityHandler && ( iState != EUsbPersonalityStarted ) )
        {
        delete iCurrentPersonalityHandler;
        iCurrentPersonalityHandler = NULL;
        }

    // if started then this must unload confirmation
    if( iState != EUsbPersonalityStarted )
        {
        iState = EUsbPersonalityIdle;
        }

    // When cancel happens it means that we can cancel all queries & notes
    iPersonalityParams->PersonalityNotifier().CancelAll();

    User::RequestComplete( iRequestStatus, KErrCancel );
    }

// ----------------------------------------------------------------------------
// Creates and returns a class handler object for the given personality.
// ----------------------------------------------------------------------------
//
CUsbPersonality* CUsbActivePersonalityHandler::NewPersonalityHandlerL(
        TInt aPersonality )
    {
    LOG_FUNC

    TInt personalityCount = iOwner.Personalities().Count();

    for (TInt i = 0; i < personalityCount; i++)
        {
        const TUsbSupportedPersonalityInf& pinf = iOwner.Personalities()[i];
        if( pinf.iPersonalityId == aPersonality )
            {
            iPersonalityParams->SetPersonalityId( aPersonality );
            CUsbPersonalityPlugin* plugin = CUsbPersonalityPlugin::NewL(
                    *iPersonalityParams, pinf.iPersonalityUid );
            iUseSerialNumber = pinf.iUseSerialNumber;
            return plugin;
            }
        }

    return NULL;
    }

// ----------------------------------------------------------------------------
// Publish serial number for USB Manager, if needed.
// ----------------------------------------------------------------------------
//
TInt CUsbActivePersonalityHandler::PublishSerialNumber()
    {
    LOG_FUNC

    TInt err = KErrNone;

    if( !iUseSerialNumber && iSerialNumberWritten )
        {
        // We are either in test mode or going to start up PC Suite
        // personality -> delete USB Manager's serial number P&S key
        // (if necessary)
        LOG( "Deleting published USB serial number" );
        err = RProperty::Delete( KUidSystemCategory, KUsbmanSvrUid.iUid );
        iSerialNumberWritten = EFalse;
        }
    else if( iUseSerialNumber && !iSerialNumberWritten )
        {
        // to finish, define and write the serial number P&S so that USB
        // Manager can fetch it
        _LIT_SECURITY_POLICY_PASS( KAPReadPolicy );
        _LIT_SECURITY_POLICY_PASS( KAPWritePolicy );

        err = RProperty::Define( KUidSystemCategory, KUsbmanSvrUid.iUid,
                RProperty::EText, KAPReadPolicy, KAPWritePolicy,
                KUsbStringDescStringMaxSize );

        if( !err )
            {
            err = RProperty::Set( KUidSystemCategory, KUsbmanSvrUid.iUid,
                    iPhoneInfo.iSerialNumber );
            iSerialNumberWritten = ( err == KErrNone );
            }
        }

    LOG1(" ret = %d", err );

    return err;
    }

// ----------------------------------------------------------------------------
// Cancel cable connected notifier
// ----------------------------------------------------------------------------
//
void CUsbActivePersonalityHandler::CancelCableConnectedNotifier()
    {
    LOG_FUNC

    iPersonalityParams->PersonalityNotifier().CancelQuery(
            KCableConnectedNotifierUid );
    }

// End of file