srsf/vcommandhandler/src/vcommandservices.cpp
branchRCL_3
changeset 19 e36f3802f733
parent 0 bf1d17376201
equal deleted inserted replaced
18:cad71a31b7fc 19:e36f3802f733
       
     1 /*
       
     2 * Copyright (c) 2006-2007 Nokia Corporation and/or its subsidiary(-ies). 
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Implementation of VCommand Handler that links the VCommand 
       
    15 *                app to VAS
       
    16 *
       
    17 */
       
    18 
       
    19 
       
    20 #include "rubydebug.h"
       
    21 #include "vcommandservices.h"
       
    22 #include "contextprovider.h"
       
    23 #include "tagnameset.h"
       
    24 #include "taggetter.h"
       
    25 #include "tagcommandconverter.h"
       
    26 #include "tagplayer.h"
       
    27 #include <nssvascoreconstant.h>
       
    28 
       
    29 #include <s32mem.h>
       
    30 #include <nssvascvasdbmgr.h>
       
    31 #include "vcommandinternalapi.h"
       
    32 
       
    33 // For CleanupResetAndDestroy
       
    34 #include <mmfcontrollerpluginresolver.h>  
       
    35 
       
    36 
       
    37 /**
       
    38 * Name of the global mutex used to serialize the operations
       
    39 */
       
    40 _LIT( KCommandHandlerLockName, "VCOMHNDLR" );
       
    41 
       
    42 _LIT( KCommandHandlerPanic, "VCH" );
       
    43 
       
    44 TAny* TVCommandTagPtrArrayKey::At( TInt aIndex ) const
       
    45     {
       
    46     MNssTag* tag ( NULL );
       
    47     TInt* key (NULL);
       
    48     if ( aIndex==KIndexPtr )
       
    49         {
       
    50         // iPtr is a pointer to the array element which is a pointer itself 
       
    51         // in case of the CArrayPtr
       
    52         tag = *(MNssTag**)iPtr;
       
    53         key = &tag->RRD()->IntArray()->At( 0 );
       
    54         }
       
    55     else 
       
    56         {
       
    57         // iBase is CBufBase* of the searched array
       
    58         // CBufBase memory buffer stores pointers to MNssTag* (i.e. MNssTag**)
       
    59         // iBase->Ptr returns TPtr8 to the desired pointer 
       
    60         // TPtr8.Ptr returns TUint8* of the element
       
    61         // This element itself is a pointer to CPerson (i.e. MNssTag**)
       
    62         tag = *(MNssTag**)iBase->Ptr( aIndex*sizeof( MNssTag** ) ).Ptr();
       
    63         key = &tag->RRD()->IntArray()->At( 0 );
       
    64         }        
       
    65         return key;        
       
    66     }
       
    67     
       
    68 // Construction/destruction
       
    69 CVCommandService* CVCommandService::NewL( MVCommandHandlerObserver* aObserver )
       
    70     {
       
    71     CVCommandService* self = new (ELeave) CVCommandService( aObserver );
       
    72     CleanupStack::PushL( self );
       
    73     self->ConstructL();
       
    74     CleanupStack::Pop( self );
       
    75     return self;
       
    76     }
       
    77     
       
    78 CVCommandService::~CVCommandService()
       
    79     {
       
    80     RUBY_DEBUG0( "CVCommandService::~CVCommandService" );
       
    81     delete iGlobalTickWatcher;
       
    82     iGlobalTickProperty.Close();
       
    83     delete iVasDbManager;
       
    84     iCommands.ResetAndDestroy();
       
    85     delete iTagPlayer;
       
    86     if( iLock.IsHeld() )
       
    87         {
       
    88         RUBY_DEBUG0( "CVCommandService::~CVCommandService. Mutex is still held.\
       
    89                      Signaling it" );
       
    90         iLock.Signal();
       
    91         }
       
    92     iLock.Close();
       
    93     }
       
    94    
       
    95 // From the MVCService
       
    96 
       
    97 /**
       
    98 * Synchronous. Service doesn't take the ownership, but makes an own copy
       
    99 * Duplicates can be added
       
   100 * @todo should we check for duplicates and leave with KErrAlreadyExists?
       
   101 */
       
   102 void CVCommandService::AddCommandL( const CVCommand& aCommand )
       
   103     {
       
   104     RUBY_DEBUG_BLOCK( "CVCommandService::AddCommandL" );
       
   105     StartAtomicOperationLC();
       
   106     
       
   107     RVCommandArray commands;
       
   108     CleanupClosePushL( commands );
       
   109     commands.Append( &aCommand );
       
   110     DoAddCommandsL( commands );
       
   111     
       
   112     CleanupStack::PopAndDestroy( &commands );
       
   113     CleanupStack::PopAndDestroy();  // Calls EndAtomicOperation
       
   114     }
       
   115 
       
   116 /**
       
   117 * Synchronous. Service doesn't take the ownership, but makes an own copy
       
   118 * Duplicates can be added
       
   119 * @todo Should we check for duplicates and leave with KErrAlreadyExists?
       
   120 * @param aIgnoreErrors If ETrue, even if some commands fail to be trained,
       
   121 *        handler adds all that are trainable
       
   122 */
       
   123 void CVCommandService::AddCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors )
       
   124     {
       
   125     RUBY_DEBUG_BLOCK( "CVCommandService::AddCommandsL" );
       
   126     StartAtomicOperationLC();
       
   127     DoAddCommandsL( aCommands, aIgnoreErrors );
       
   128     CleanupStack::PopAndDestroy();  // Calls EndAtomicOperation
       
   129     }
       
   130     
       
   131 void CVCommandService::DoAddCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors )
       
   132     {
       
   133     RUBY_DEBUG_BLOCKL( "CVCommandService::DoAddCommandsL" );
       
   134     MNssContext* context = GetVCommandContextLC();
       
   135     CTagNameSet* trainingPack = CTagNameSet::NewLC();
       
   136     
       
   137     for( TInt i = 0; i < aCommands.Count(); i++ )
       
   138         {
       
   139         CArrayPtr<MNssTag>* tags = CreateTagsLC2( *aCommands[i], *context); 
       
   140         // tags array contains 1 tag if UserText is not defined
       
   141         // tags array contains 2==KMaxTagsPerCommand tags if UserText is defined
       
   142         __ASSERT_ALWAYS( tags->Count() > 0, User::Leave( KErrCorrupt ) );
       
   143         __ASSERT_ALWAYS( tags->Count() <= KMaxTagsPerCommand, User::Leave( KErrCorrupt ) );
       
   144         
       
   145         // All the tags corresponding to the same VCommand get the same command id
       
   146         SetVCommandIdL( *tags, NewCommandIdL() );
       
   147         trainingPack->AppendL( tags->At( 0 ), aCommands[i]->SpokenText() );
       
   148         if( tags->Count() == KMaxTagsPerCommand )
       
   149             {
       
   150             // UserText tag is present and this command has been converted into two tags
       
   151             trainingPack->AppendL( tags->At( 1 ), aCommands[i]->AlternativeSpokenText() );
       
   152             }    
       
   153             
       
   154         CleanupStack::Pop( tags );  // ResetAndDestroy
       
   155         CleanupStack::PopAndDestroy( tags );  // delete the CArrayPtr itself
       
   156         }  // for aCommands
       
   157     
       
   158     
       
   159     CNssTrainingParameters* trainingParams = ConstructTrainingParametersLC();    
       
   160     trainingPack->TrainAndSaveL( *trainingParams, *iTagManager, aIgnoreErrors );
       
   161     CleanupStack::PopAndDestroy( trainingParams );
       
   162     
       
   163     CleanupStack::PopAndDestroy( trainingPack );
       
   164     CleanupStack::PopAndDestroy( context );
       
   165     RUBY_DEBUG0( "CVCH::DoAddCommandsL Context destroyed" );        
       
   166     InvalidateCacheL();
       
   167     }
       
   168 
       
   169 
       
   170 /**
       
   171 * Synchronous. Removes the command from the system
       
   172 * @param aCommand Reference to the command to be removed. Existing commands are
       
   173 *		 compared against aCommand. All the matches are removed from VAS
       
   174 * @leave KErrNotFound No such command
       
   175 */
       
   176 void CVCommandService::RemoveCommandL( const CVCommand& aCommand )
       
   177     {
       
   178     RUBY_DEBUG_BLOCK( "CVCommandService::RemoveCommandL" );
       
   179     StartAtomicOperationLC();
       
   180 	RVCommandArray array;
       
   181 	CleanupClosePushL( array );
       
   182 	array.AppendL( &aCommand );
       
   183 	DoRemoveCommandsL( array );
       
   184 	CleanupStack::PopAndDestroy( &array );
       
   185     CleanupStack::PopAndDestroy();  // Calls EndAtomicOperation
       
   186     }
       
   187     
       
   188 /**
       
   189 * Synchronous. 
       
   190 * @param aCommands Reference to the list of commands to be removed. Existing commands are
       
   191 *		 compared against aCommands items. All the matches are removed from VAS
       
   192 * @param aIgnoreErrors If ETrue, even if some commands fail to be removed,
       
   193 *        handler will remove as many as possible
       
   194 */
       
   195 void CVCommandService::RemoveCommandsL( const RVCommandArray& aCommands, TBool aIgnoreErrors )
       
   196 	{
       
   197     RUBY_DEBUG_BLOCK( "RemoveCommandsL" );
       
   198     StartAtomicOperationLC();
       
   199     DoRemoveCommandsL( aCommands, aIgnoreErrors );
       
   200     CleanupStack::PopAndDestroy();  // Calls EndAtomicOperation
       
   201 	
       
   202 	}
       
   203     
       
   204 void CVCommandService::DoRemoveCommandsL( const RVCommandArray& aCommands,
       
   205         						TBool aIgnoreErrors )
       
   206 	{
       
   207 	// for tracking the not found situations
       
   208 	RArray<TBool> foundCommands;
       
   209 	CleanupClosePushL( foundCommands );
       
   210 	for( TInt i=0; i < aCommands.Count(); i++ )
       
   211 	    {
       
   212 	    foundCommands.AppendL( EFalse );
       
   213 	    }
       
   214 	
       
   215 	// Iterate over all the tags in the system. Whenever a tag can be converted
       
   216 	// to one of the aCommands, move it to the deletion bunch
       
   217 	CTagNameSet* removeTags = CTagNameSet::NewLC();
       
   218 	MNssContext* context = GetVCommandContextLC();    
       
   219 	MNssTagListArray* tags = CVCommandService::GetTagListLC2( *context );
       
   220 	for( TInt i = 0; i < tags->Count(); i++ )
       
   221 		{
       
   222 		CVCommand* command = CTagCommandConverter::TagToCommandLC( *tags->At( i ) );
       
   223 		for( TInt j = 0; j < aCommands.Count(); j++ )
       
   224 			{
       
   225 			if( *command == *aCommands[j] )
       
   226 				{
       
   227 				RUBY_DEBUG1( "CVCH::RemoveCommandsL Tag [%d] should be deleted", i );
       
   228 				foundCommands[j] = ETrue;
       
   229 				removeTags->AppendL( tags->At( i ) );  
       
   230 				// ownership to removeTags, current tag to be removed from tags => index--
       
   231 				tags->Delete( i );
       
   232 				i--;
       
   233 				break;
       
   234 				}
       
   235 			}
       
   236 		CleanupStack::PopAndDestroy( command );
       
   237 		}
       
   238 
       
   239     // Handle not founds
       
   240     if( !aIgnoreErrors )
       
   241         {
       
   242         for( TInt i=0; i < foundCommands.Count(); i++ )
       
   243             {
       
   244             if( foundCommands[i] == EFalse )
       
   245                 {
       
   246                 RUBY_ERROR1( "Command [%d] not found", i );
       
   247                 User::Leave( KErrNotFound );
       
   248                 }
       
   249             }
       
   250         }
       
   251 	// Delete all the discovered tags
       
   252 	removeTags->UntrainL( *iTagManager, aIgnoreErrors );
       
   253 	
       
   254 	CleanupStack::PopAndDestroy( tags );  // ResetAndDestroy
       
   255 	CleanupStack::PopAndDestroy( tags );  // delete	
       
   256     CleanupStack::PopAndDestroy( context );
       
   257     CleanupStack::PopAndDestroy( removeTags );
       
   258     CleanupStack::PopAndDestroy( &foundCommands );
       
   259     InvalidateCacheL();
       
   260 	}
       
   261         						
       
   262 /**
       
   263 * Synchronous
       
   264 * @return an array of the commands in the system
       
   265 *         Ownership of the array is transfered to the client
       
   266 *         The returned CVCommandArray contains copies of all the 
       
   267 *         commands currently stored in this handler
       
   268 */
       
   269 CVCommandArray* CVCommandService::ListCommandsL()
       
   270     {
       
   271     RUBY_DEBUG_BLOCK( "CVCommandService::ListCommandsL" );
       
   272     StartAtomicOperationLC();
       
   273     if( !IsCacheValidL() )
       
   274     	{
       
   275     	RUBY_DEBUG0( "CVCommandService::ListCommandsL Cache is invalid. \
       
   276     					Refresh the command list" );
       
   277     	RefreshCommandListL();
       
   278     	}
       
   279     
       
   280     CVCommandArray* result = CVCommandArray::NewL( iCommands );
       
   281     CleanupStack::PopAndDestroy();  // Calls EndAtomicOperation
       
   282     return result;
       
   283     }
       
   284 
       
   285 // Private methods
       
   286 
       
   287 CVCommandService::CVCommandService( MVCommandHandlerObserver* aObserver ) :
       
   288     iObserver( aObserver )
       
   289     {
       
   290     // nothing
       
   291     }
       
   292 
       
   293 void CVCommandService::ConstructL()
       
   294     {
       
   295     RUBY_DEBUG_BLOCKL( "CVCommandService::ConstructL" );
       
   296     TInt err = iLock.OpenGlobal( KCommandHandlerLockName );
       
   297     if( err != KErrNone )
       
   298         {
       
   299         RUBY_DEBUG1( "CVCommandService::ConstructL failed to open mutex with [%d].\
       
   300                       Creating a new one", err );
       
   301         err = iLock.CreateGlobal( KCommandHandlerLockName );
       
   302         RUBY_DEBUG1( "CVCommandService::ConstructL. Created mutex. Err [%d]", err );
       
   303         User::LeaveIfError( err );
       
   304         }
       
   305     iTagPlayer = CTagPlayer::NewL();
       
   306     iVasDbManager = CNssVASDBMgr::NewL();
       
   307     iVasDbManager->InitializeL();
       
   308     iTagManager = iVasDbManager->GetTagMgr();
       
   309     TInt propertyErr = RProperty::Define( KSINDUID, ECommandHandlerTickKey, RProperty::EInt );
       
   310     if( ( propertyErr != KErrNone ) && ( propertyErr != KErrAlreadyExists ) )
       
   311         {
       
   312         RUBY_ERROR1( "Attempt to define the ECommandHandlerTickKey flag failed with [%d]", 
       
   313                      propertyErr )
       
   314         User::Leave( propertyErr );
       
   315         }
       
   316     
       
   317     RUBY_DEBUG0( "Attaching to the global tick property" );
       
   318     User::LeaveIfError( iGlobalTickProperty.Attach( KSINDUID, ECommandHandlerTickKey ) );
       
   319     // right after creation, local cache cannot be valid
       
   320     TInt globalTickCount;
       
   321     User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) );
       
   322     iLocalTickCount = globalTickCount - 1;
       
   323     
       
   324     iLastSetGlobalTickCount = iLocalTickCount;
       
   325     iGlobalTickWatcher = CIntPropertyWatcher::NewL( KSINDUID, ECommandHandlerTickKey, *this );
       
   326     }
       
   327     
       
   328 /**
       
   329 * Returns the VCommand context. Creates one on demand if none exists yet
       
   330 */
       
   331 MNssContext* CVCommandService::GetVCommandContextLC() const
       
   332     {
       
   333     RUBY_DEBUG0( "CVCommandService::GetVCommandContextL start" );
       
   334     CContextProvider* contextProvider = CContextProvider::NewLC( *iVasDbManager );
       
   335     MNssContext* result = contextProvider->GetVCommandContextLC();
       
   336     CleanupStack::Pop( result );
       
   337     CleanupStack::PopAndDestroy( contextProvider );
       
   338     CleanupDeletePushL( result );
       
   339     RUBY_DEBUG0( "CVCommandService::GetVCommandContextL end" );
       
   340     return result;    
       
   341     }
       
   342     
       
   343     
       
   344 /**
       
   345 * Constructs a set of new VAS tags from a command and a context. 
       
   346 * Does not save to VAS, just creates. Ownership on the created tag is 
       
   347 * passed to the client
       
   348 */
       
   349 CArrayPtr<MNssTag>* CVCommandService::CreateTagsLC2( const CVCommand& aCommand, 
       
   350                                                      const MNssContext& aContext )
       
   351     {
       
   352     return CTagCommandConverter::CommandToTagsLC2( aCommand, aContext, *iTagManager );
       
   353     }
       
   354     
       
   355 /**
       
   356 * Constructs training related parameters. Like language to be used
       
   357 * @leave negated TNssSpeechItemResult
       
   358 * @todo Is it ok to mix system-wide codes with the TNssSpeechItemResult codes
       
   359 */
       
   360 CNssTrainingParameters* CVCommandService::ConstructTrainingParametersLC() const
       
   361     {
       
   362     RUBY_DEBUG0( "CVCommandService::SetLanguageParamersL start" );
       
   363     RArray<TLanguage>* languageArray = new (ELeave) RArray<TLanguage>;
       
   364     CleanupDeletePushL ( languageArray ); // Protect [allocated on the heap] array itself
       
   365     CleanupClosePushL( *languageArray );  // Protect the array elements
       
   366     
       
   367     // Always generate a pronunciation in UI language
       
   368     User::LeaveIfError( languageArray->Append( User::Language() ) );
       
   369     
       
   370     // Add an extra language, which can be determined by language identification
       
   371     User::LeaveIfError( languageArray->Append( ELangOther ) );
       
   372 
       
   373     CNssTrainingParameters* trainingParams = CNssTrainingParameters::NewL();
       
   374     // No L-functions for some time, delay pushing parameters to the cleanup stack
       
   375     
       
   376     trainingParams->SetLanguages( languageArray );
       
   377     // No need to protect the language array anymore. 
       
   378     // From now on it is managed by the parameters 
       
   379     
       
   380     // For array elements pushed via CleanupClosePushL
       
   381     CleanupStack::Pop( languageArray );
       
   382     CleanupStack::Pop( languageArray );
       
   383     CleanupStack::PushL( trainingParams );
       
   384     RUBY_DEBUG0( "CVCommandService::SetLanguageParamersL end" );
       
   385     return trainingParams;    
       
   386     }
       
   387 
       
   388 /**
       
   389 * Retrieves the list of tags for a given context. Synchronous.
       
   390 * Leaves two objects on the cleanup stack!
       
   391 * First PopAndDestroy will ResetAndDestroy content
       
   392 * Second one will destroy the MNsstagListArray itself
       
   393 */
       
   394 MNssTagListArray* CVCommandService::GetTagListLC2( const MNssContext& aContext ) const
       
   395     {
       
   396     RUBY_DEBUG0( "CVCommandService::GetTagListLC start" );
       
   397     CTagGetter* tagGetter = CTagGetter::NewLC();
       
   398     MNssTagListArray* result = tagGetter->GetTagListLC2( *iTagManager, aContext );
       
   399 
       
   400     CleanupStack::Pop( result );
       
   401     CleanupStack::Pop( result );
       
   402     CleanupStack::PopAndDestroy( tagGetter );
       
   403     CleanupDeletePushL( result );
       
   404     CleanupResetAndDestroy<MNssTagListArray>::PushL( *result );
       
   405     RUBY_DEBUG0( "CVCommandService::GetTagListLC end" );
       
   406 
       
   407     return result;
       
   408     }
       
   409     
       
   410 /**
       
   411 * Retrieves the list of tags for a given context and voice command id. 
       
   412 * Synchronous. Leaves two objects on the cleanup stack!
       
   413 * First PopAndDestroy will ResetAndDestroy content
       
   414 * Second one will destroy the MNsstagListArray itself
       
   415 */
       
   416 MNssTagListArray* CVCommandService::GetTagListLC2( const MNssContext& aContext, 
       
   417                                                    TInt aCommandId ) const
       
   418     {
       
   419     RUBY_DEBUG0( "CVCommandService::GetTagListLC start" );
       
   420     CTagGetter* tagGetter = CTagGetter::NewLC();
       
   421     MNssTagListArray* result = tagGetter->GetTagListLC2( *iTagManager, aContext, aCommandId );
       
   422 
       
   423     CleanupStack::Pop( result );
       
   424     CleanupStack::Pop( result );
       
   425     CleanupStack::PopAndDestroy( tagGetter );
       
   426     CleanupDeletePushL( result );
       
   427     CleanupResetAndDestroy<MNssTagListArray>::PushL( *result );
       
   428     RUBY_DEBUG0( "CVCommandService::GetTagListLC end" );
       
   429 
       
   430     return result;
       
   431     }
       
   432 
       
   433 /**
       
   434 * Resets iCommands and fills them with the commands from VAS
       
   435 */
       
   436 void CVCommandService::RefreshCommandListL()
       
   437 	{
       
   438 	RUBY_DEBUG_BLOCK( "CVCommandService::RefreshCommandListL" );
       
   439 	const TInt KMinimalCommandId = 0;
       
   440 	iCommands.ResetAndDestroy();
       
   441 	iMaxCommandId = KMinimalCommandId - 1;
       
   442     MNssContext* context = GetVCommandContextLC();
       
   443     MNssTagListArray* tagList = GetTagListLC2( *context );
       
   444     RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Found [%d] tags", tagList->Count() );
       
   445     
       
   446     // Sort array by command ids
       
   447     TVCommandTagPtrArrayKey key( ECmpTInt );
       
   448     tagList->Sort( key );
       
   449     
       
   450     // Array of tags related to the same VCommand
       
   451     CArrayPtrFlat<MNssTag>* commandArray = 
       
   452                 new ( ELeave) CArrayPtrFlat<MNssTag>( KMaxTagsPerCommand ); 
       
   453     CleanupStack::PushL( commandArray );
       
   454     // No need for CleanupResetAndDestroyPushL. 
       
   455     // MNssTag objects are not copied to the commandArray
       
   456     
       
   457     TInt currentId = KMinimalCommandId - 1;
       
   458     
       
   459     for( TInt i = 0; i < tagList->Count(); i++ ) 
       
   460         {
       
   461         RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Processing tag [%d]", i );
       
   462         TInt commandId = tagList->At( i )->RRD()->IntArray()->At( 0 );
       
   463         if( currentId != commandId )  // new or first command
       
   464             {
       
   465             if( commandArray->Count() > 0  )
       
   466                 {
       
   467                 RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Converting [%d] tags to command", commandArray->Count() );
       
   468                 CStoredVCommand* command = CTagCommandConverter::TagsToCommandLC( *commandArray );
       
   469                 RUBY_DEBUG2( "CVCommandService::RefreshCommandListL Converted successfully to command id [%d], command text [%S]", command->CommandId(), &(command->SpokenText() ) );
       
   470                 iCommands.AppendL( command );
       
   471                 CleanupStack::Pop( command );
       
   472                 }
       
   473             commandArray->Reset();
       
   474             currentId = commandId;
       
   475             }
       
   476         commandArray->AppendL( tagList->At( i ) );
       
   477         iMaxCommandId = Max( iMaxCommandId, commandId );
       
   478         }
       
   479         
       
   480     // Flush the last buffer
       
   481     if( commandArray->Count() > 0  )
       
   482         {
       
   483         RUBY_DEBUG1( "CVCommandService::RefreshCommandListL Converting [%d] tags to command", commandArray->Count() );
       
   484         CStoredVCommand* command = CTagCommandConverter::TagsToCommandLC( *commandArray );
       
   485         RUBY_DEBUG2( "CVCommandService::RefreshCommandListL Converted successfully to command id [%d], command text [%S]", command->CommandId(), &(command->SpokenText() ) );
       
   486         iCommands.AppendL( command );
       
   487         CleanupStack::Pop( command );
       
   488         }
       
   489     CleanupStack::PopAndDestroy( commandArray );
       
   490     CleanupStack::PopAndDestroy( tagList );  // ResetAndDestroy
       
   491     CleanupStack::PopAndDestroy( tagList );  // delete
       
   492     CleanupStack::PopAndDestroy( context );
       
   493     MarkCacheValidL();
       
   494 	}
       
   495 	
       
   496 /**
       
   497 * Marks the iCommands as an invalid reflection of the VAS content.
       
   498 * To make iCommands reflect the real VAS content a call to 
       
   499 * RefreshCommandsL is needed
       
   500 */
       
   501 void CVCommandService::InvalidateCacheL()
       
   502 	{
       
   503 	// Invalidating own cache means unvalidating the other instances' caches as well
       
   504 	TInt globalTickCount;
       
   505 	User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) );
       
   506 	
       
   507 	iLocalTickCount = globalTickCount;
       
   508     iLastSetGlobalTickCount = globalTickCount + 1;
       
   509 	User::LeaveIfError( iGlobalTickProperty.Set( iLastSetGlobalTickCount ) );
       
   510 	}
       
   511 	
       
   512 /**
       
   513 * Marks the iCommands as the valid reflection of the VAS content.
       
   514 */
       
   515 void CVCommandService::MarkCacheValidL()
       
   516 	{
       
   517 	// Since all the cache comparison/update functions are executed under the
       
   518 	// StartAtomicOperation/EndAtomicOperation, we can be sure that nobody
       
   519 	// touched VAS and global tick count since the last IsCacheValidL
       
   520 	TInt globalTickCount;
       
   521 	User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) );
       
   522 	
       
   523 	iLocalTickCount = globalTickCount;
       
   524 	}
       
   525 
       
   526 /**
       
   527 * Tells if the iCommands reflect the content of the VAS DB correctly
       
   528 * @return ETrue if the cache is valid, EFalse otherwise
       
   529 */
       
   530 TBool CVCommandService::IsCacheValidL()
       
   531 	{
       
   532 	RUBY_DEBUG_BLOCKL( "CVCommandService::IsCacheValidL" );
       
   533 	TInt globalTickCount;
       
   534 	User::LeaveIfError( iGlobalTickProperty.Get( globalTickCount ) );
       
   535 	RUBY_DEBUG2( "CVCommandService::IsCacheValidL Local tick count [%d], \
       
   536 	              globalTickCount [%d]", iLocalTickCount, globalTickCount );
       
   537 	return iLocalTickCount == globalTickCount;
       
   538 	}
       
   539 
       
   540 /**
       
   541 * Generates new command id to be used for identifying voice tags, that
       
   542 * belong to the same VCommand
       
   543 */
       
   544 TInt CVCommandService::NewCommandIdL()
       
   545     {
       
   546     if( !IsCacheValidL() )
       
   547         {
       
   548         // Fills the max used command id
       
   549         RefreshCommandListL();
       
   550         }
       
   551     return ++iMaxCommandId;
       
   552     }
       
   553     
       
   554 /** 
       
   555 * Sets the given aId as a VCommand id for all the tags in the given list
       
   556 * This id is set as a first RRD int element of all the given MNssTags.
       
   557 * If the RRD int array has no elements yet, one element is added
       
   558 */
       
   559 void CVCommandService::SetVCommandIdL( CArrayPtr<MNssTag>& aTags, TInt aId ) const
       
   560     {
       
   561     // All the tags corresponding to the same VCommand get the same command id
       
   562     
       
   563     for( TInt i = 0; i < aTags.Count(); i++ )
       
   564         {
       
   565         if( aTags[i]->RRD()->IntArray()->Count() > 0 ) 
       
   566             {
       
   567             aTags[i]->RRD()->IntArray()->At( i ) = aId;
       
   568             }
       
   569         else 
       
   570             {
       
   571             aTags[i]->RRD()->IntArray()->AppendL( aId );
       
   572             }
       
   573         }
       
   574      }
       
   575 
       
   576 /**
       
   577 * Asynchronous
       
   578 * Attempts to play back the text expected to be recognized. 
       
   579 * To be playable command has to be added to CVCommandHandler AND
       
   580 * then retrieved back
       
   581 *
       
   582 * @param aHandler CVCommandHandler where the command is stored
       
   583 * @param aPlayEventHandler Entity that handles the playback callbacks
       
   584 * @see NssVasMPlayEventHandler.h
       
   585 *
       
   586 * @leave KErrBadHandle if the current command has not been retrieved 
       
   587 *        from CVCommandHandler (i.e. was not trained for recognition)
       
   588 * @leave KErrNotFound if this command cannot be found in aHandler
       
   589 * @leave KErrNotReady @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult 
       
   590 *                                              EVasUnexpectedRequest
       
   591 * @leave KErrInUse @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult 
       
   592 *                                              EVasInUse
       
   593 * @leave KErrArgument @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult 
       
   594 *                                              EVasInvalidParameter
       
   595 * @leave KErrGeneral @see nssvasmspeechitem.h MNssSpeechItem::TNssSpeechItemResult 
       
   596 *                                             EVasPlayFailed
       
   597 */
       
   598 void CVCommandService::PlaySpokenTextL( const CStoredVCommand& aCommand, 
       
   599                                         MNssPlayEventHandler& aPlayEventHandler )
       
   600     {
       
   601     RUBY_DEBUG_BLOCK( "CVCommandService::PlaySpokenTextL" );
       
   602     __ASSERT_ALWAYS( !iPlaybackHandler, User::Leave( KErrInUse  ) );
       
   603     StartAtomicOperationLC();
       
   604     iPlaybackHandler = &aPlayEventHandler;
       
   605     MNssTag* playbackTag = TagByCommandIdTextL( aCommand.CommandId(), 
       
   606                                     aCommand.SpokenText() );
       
   607     if( !playbackTag )
       
   608         {
       
   609         User::Leave( KErrNotFound );
       
   610         }
       
   611     iTagPlayer->PlayTagL( playbackTag, *this ); 
       
   612     CleanupStack::Pop(); // EndOfAtomicOperation
       
   613     }
       
   614 
       
   615 /**
       
   616 * Asynchronous
       
   617 * Plays back the user-specified alternative spoken text. 
       
   618 * Otherwise is identical to PlaySpokenTextL
       
   619 * @see PlaySpokenTextL
       
   620 * @leave KErrNotFound if this command cannot be found in aHandler of if 
       
   621 *        it doesn't have a user-specified text
       
   622 */     
       
   623 void CVCommandService::PlayAlternativeSpokenTextL( const CStoredVCommand& aCommand, 
       
   624                                         MNssPlayEventHandler& aPlayEventHandler )
       
   625     {
       
   626     RUBY_DEBUG_BLOCK( "CVCommandService::PlayAlternativeSpokenTextL" );
       
   627     __ASSERT_ALWAYS( !iPlaybackHandler, User::Leave( KErrInUse  ) );
       
   628     StartAtomicOperationLC();
       
   629     iPlaybackHandler = &aPlayEventHandler;
       
   630     MNssTag* playbackTag = TagByCommandIdTextL( aCommand.CommandId(), 
       
   631                                     aCommand.AlternativeSpokenText() );
       
   632     if( !playbackTag )
       
   633         {
       
   634         User::Leave( KErrNotFound );
       
   635         }
       
   636     iTagPlayer->PlayTagL( playbackTag, *this ); 
       
   637     CleanupStack::Pop(); // EndOfAtomicOperation
       
   638     }
       
   639     
       
   640 /**
       
   641 * This method is not intended to be called directly. 
       
   642 * Use CVCommand::CancelPlaybackL instead
       
   643 * @see CVCommand::CancelPlaybackL
       
   644 * @see CStoredVCommand::CancelPlaybackL
       
   645 */
       
   646 void CVCommandService::CancelPlaybackL( const CStoredVCommand& /*aCommand*/ )
       
   647     {
       
   648     // For calling EndOfAtomicOperation if a leave occurs 
       
   649     CreateAndPushEndAtomicCleanupItemL();
       
   650 
       
   651     __ASSERT_ALWAYS( iPlaybackHandler, User::Leave( KErrNotReady ) );
       
   652     iTagPlayer->CancelPlaybackL();
       
   653     iPlaybackHandler = NULL;
       
   654     CleanupStack::PopAndDestroy(); // Calls EndAtomicOperation
       
   655     }
       
   656     
       
   657 /**
       
   658 * The HandlePlayStarted method is a virtual method implemented by the
       
   659 * client and is called when play is started
       
   660 * @param aDuration - the duration of the utterance data
       
   661 */       
       
   662 void CVCommandService::HandlePlayStarted( TTimeIntervalMicroSeconds32 aDuration )
       
   663     {
       
   664     __ASSERT_ALWAYS( iPlaybackHandler, User::Panic( KCommandHandlerPanic, KErrNotReady ) );
       
   665     iPlaybackHandler->HandlePlayStarted( aDuration );
       
   666     }
       
   667 
       
   668 /**
       
   669 * The HandlePlayComplete method is a virtual method implemented by the
       
   670 * client and is called when play is completed
       
   671 * @param aErrorCode EVasErrorNone if playing was successfull
       
   672 */       
       
   673 void CVCommandService::HandlePlayComplete( TNssPlayResult aErrorCode )
       
   674     {
       
   675     __ASSERT_ALWAYS( iPlaybackHandler, User::Panic( KCommandHandlerPanic, KErrNotReady ) );
       
   676     
       
   677     // End atomic operation before calling the observer not to block its potential
       
   678     // VCommandHandler usage from the HandlePlayComplete
       
   679     
       
   680     // Before ending atomic operation, store the current event handler to the 
       
   681     // temporary variable to protect it against the potential change by the new
       
   682     // PlaySpokenTextL call
       
   683     MNssPlayEventHandler* handler = iPlaybackHandler;
       
   684     iPlaybackHandler = NULL;
       
   685     EndAtomicOperation();
       
   686     handler->HandlePlayComplete( aErrorCode );    
       
   687     }
       
   688 
       
   689     
       
   690 /**
       
   691 * Searches vcommand tags for the tag, that corresponds to the given
       
   692 * commandId and is trained against the given text
       
   693 * @param aCommandId Command id to search for
       
   694 * @param aText
       
   695 * @return ANY of the voice tags corresponding to the given aCommandId
       
   696 *         and aText. Typically there will be one such tag only. However,
       
   697 *         if there are several it is not specified which one will be 
       
   698 *         returned
       
   699 * @return NULL if no satisfying tag is found
       
   700 */
       
   701 MNssTag* CVCommandService::TagByCommandIdTextL( TInt aCommandId, const TDesC& aText )
       
   702     {
       
   703     RUBY_DEBUG_BLOCKL( "CVCommandService::TagByCommandIdTextL" );
       
   704     MNssContext* context = GetVCommandContextLC();
       
   705     MNssTagListArray* tagList = GetTagListLC2( *context, aCommandId );
       
   706     // A command can have up to two voice tags. We need to locate the one, 
       
   707     // that has been trained against a given text
       
   708     MNssTag* tag (NULL);
       
   709     for( TInt i = 0; i < tagList->Count(); i++ )
       
   710         {
       
   711         if( tagList->At(i)->SpeechItem()->RawText() == aText )
       
   712             {
       
   713             tag = tagList->At(i);
       
   714             // deletes pointer only. tag will be returned to the calling party
       
   715             tagList->Delete( i );  
       
   716             break;
       
   717             }  // if
       
   718         }  // for
       
   719     CleanupStack::PopAndDestroy( tagList );  // ResetAndDestroy
       
   720     CleanupStack::PopAndDestroy( tagList );  // delete
       
   721     CleanupStack::PopAndDestroy( context );
       
   722     
       
   723     return tag;
       
   724     }
       
   725 
       
   726 /**
       
   727 * Starts the section of the code, that can be simultaneously executed
       
   728 * only by a single instance of this class. Other instances have to wait
       
   729 * Same as starting the Java "synchronized" method. Puts the function
       
   730 * EndAtomicOperation to cleanup stack to make the use leave safe. Subsequent
       
   731 * call to CleanupStack::PopAndDestroy will run EndAtomicOperation.
       
   732 */
       
   733 void CVCommandService::StartAtomicOperationLC()
       
   734     {
       
   735     RUBY_DEBUG0( "CVCommandService::StartAtomicOperation start" );
       
   736     if ( iLock.IsHeld() )
       
   737         {
       
   738         User::Leave( KErrLocked );
       
   739         }
       
   740         
       
   741     iLock.Wait();
       
   742     
       
   743     CreateAndPushEndAtomicCleanupItemL();
       
   744     
       
   745     RUBY_DEBUG0( "CVCommandService::StartAtomicOperation end" );
       
   746     }
       
   747 
       
   748 /**
       
   749 * Ends the section of the code, that can be simultaneously executed
       
   750 * only by a single instance of this class. Other instances have to wait
       
   751 * Same as exiting the Java "synchronized" method
       
   752 * @leave KErrNotReady if atomic operation hasn't been started
       
   753 */
       
   754 void CVCommandService::EndAtomicOperation()
       
   755     {
       
   756     RUBY_DEBUG0( "CVCommandService::EndAtomicOperation start" );
       
   757     __ASSERT_ALWAYS( iLock.IsHeld(), User::Panic( KCommandHandlerPanic, KErrNotReady ) );
       
   758     iLock.Signal();
       
   759     RUBY_DEBUG0( "CVCommandService::EndAtomicOperation end" );
       
   760     }
       
   761     
       
   762 /**
       
   763 * Runs the EndAtomicOperation when calling CleanupStack::PopAndDestroy after
       
   764 * a call to StartAtomicOperationLC
       
   765 */    
       
   766 void CVCommandService::CleanupEndAtomicOperation( TAny* aService )
       
   767     {
       
   768     CVCommandService* service = static_cast<CVCommandService*>(aService);
       
   769     service->EndAtomicOperation();
       
   770     }
       
   771 
       
   772 /**
       
   773 * Creates and pushes to cleanup stack a TCleanupItem that calls
       
   774 * EndAtomicOperation when CleanupStack::PopAndDestroy is called
       
   775 */   
       
   776 void CVCommandService::CreateAndPushEndAtomicCleanupItemL()
       
   777     {
       
   778     TCleanupItem item( CleanupEndAtomicOperation, this );
       
   779     CleanupStack::PushL( item );
       
   780     }
       
   781     
       
   782 void CVCommandService::IntValueChanged( TInt aNewValue )
       
   783     {
       
   784     RUBY_DEBUG2( "iLocalTickCount [%d], aNewValue [%d]", iLocalTickCount, aNewValue );
       
   785     if( aNewValue != iLastSetGlobalTickCount )
       
   786         {
       
   787         if( iObserver )
       
   788             {
       
   789             iObserver->CommandSetChanged();
       
   790             }  // if iObserver
       
   791         }  // if local cache is out of date
       
   792     }
       
   793     
       
   794 //End of file