// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// f32\sfile\sf_notifier.h
//
//
#include "sf_std.h"
#include "sf_pool.h"
#include "e32hashtab.h"
#include "cl_notification.h"
#include "f32notification.h"
#ifndef SF_NOTIFIER_H
#define SF_NOTIFIER_H
/**
* Number of notifications in TFsNotificationType that the client can set
* @internalTechnology
*/
const TInt KNumRegisterableFilters = 8;
/*
* This determines the size of the CFsPool.
* Until we have read/write locks there is no point in this being more than 1.
*
* @internalTechnology
*/
const TInt KNotificationPoolSize = 1;
/**
* A CFsNotificationPathFilter is a simple class containing two HBufCs of the target to be notified about:
* 1 for the drive and path
* 1 for the filename
*
* A CFsNotificationPathFilter has a 1 to Many relationship with TFsNotificationTypeFilter
*
* @internalTechnology
*/
class CFsNotificationPathFilter
{
public:
static CFsNotificationPathFilter* NewL(const TDesC& aPath, const TDesC& aFilename);
~CFsNotificationPathFilter();
private:
void ConstructL(const TDesC& aPath, const TDesC& aFilename);
CFsNotificationPathFilter();
public:
HBufC* iPath;
HBufC* iFilename;
};
/**
* A TFsNotificationTypeFilter is a class which contains a pointer to the associated path to monitor
* and the type of notification.
*
* @internalTechnology
*/
class TFsNotificationTypeFilter
{
public:
CFsNotificationPathFilter* iPathFilter;
TFsNotification::TFsNotificationType iNotificationType;
//As we are storing filters in filter-specific
//arrays then iNotificationType is potentially redundant.
};
//These typedefs are to appease the compiler as it does not like
//nested templated arguments.
/**
* @internalTechnology
*/
typedef RArray<TFsNotificationTypeFilter> TFsNotificationTypeArray;
/**
* @internalTechnology
*/
typedef RArray<TFsNotificationTypeArray> TFsNotificationTypeDriveArray;
class CFsNotificationBlock; //forward decl.
/**
* CFsNotifyRequest is a file-server side object representation of an RFsNotify sub-session.
*
* @internalTechnology
*/
NONSHARABLE_CLASS(CFsNotifyRequest) : public CFsObject
{
public:
/*
* Active means that the client is waiting for the first notification to be sent
* The status then changes to Outstanding when further notifications are sent to the buffer
* If the server overflows when in state EOutstanding then we move to state EOutstandingOverflow.
* From EOutstandingOverflow after the next call to RequestNotifications from the client, we must move to state EInactive.
* If we do not overflow then when RequestNotifications is called we can move back to state EActive.
* EInactive is when there are no notifications outstanding and request notifications hasn't been called.
*/
enum TNotifyRequestStatus
{
EActive, //Server waiting for a notification (filters added, RequestNotifications called)
EOutstanding, //Server waiting for client to call RequestNotifications, has notification(s) to send.
EOutstandingOverflow, //Server waiting for client to call RequestNotifications, has notification(s) to send, buffer has overflowed.
EInactive //Server waiting for RequestNotification, no notifications outstanding
};
static CFsNotifyRequest* NewL();
virtual ~CFsNotifyRequest();
/*
* Returns the RArray<TFsNotificationFilter> for an index
* as returned from FsNotificationHelper::TypeToIndex()
*/
TFsNotificationTypeArray* FilterTypeList(TInt aDrive,TInt aIndex);
//Removes all filters from iFilterList
TInt RemoveFilters();
//Add single filter to this request
TInt AddFilterL(CFsNotificationPathFilter* aFilter, TUint aMask);
//Sets filter as active/inactive
void SetActive(TNotifyRequestStatus aValue);
/**
*Get the status of this request.
*@See TNotifyRequestStatus
*/
TNotifyRequestStatus ActiveStatus();
/*
* Completes and frees notification request
*
* @param aIsCancel is used to determine whether
* to write back to the client or not when aReason != KErrNone.
*
* In the case of closing the subsession you shouldn't write back to the client.
*/
void CompleteClientRequest(TInt aReason,TBool aIsCancel=EFalse);
//RfsNotify::RequestNotifications has been called
TInt SetClientMessage(const RMessage2& aClientMsg);
/*
* Called from FsNotificationManager::HandleChange(),
* this function packages the data in to a CFsNotificationBlock in preperation for
* notification of this operation to the client.
*
* Calling of this function means that we are notifying about this operation. (all checks passed)
*
* aRequest can be NULL when the request doesn't come from the file server
* (such as when a media card is removed)
*/
TInt NotifyChange(CFsClientMessageRequest* aRequest, const TDesC& aName, TFsNotification::TFsNotificationType aNotificationType, CFsNotificationBlock& aBlock);
/*
* This function performs the IPC to the client's buffer.
*/
TInt SynchroniseBuffer(CFsNotificationBlock& aBlock,TInt aServerTail, TInt aNotificationSize);
//Closing this notification
void CloseNotification();
//Simple getter
TInt ClientMsgHandle();
private:
CFsNotifyRequest();
void ConstructL();
//Check whether there is room for a new notification in the client's buffer
TBool ValidateNotification(TInt aNotificationSize, TInt& aServerTail);
/*
* The iTailSemaphore is used so that many CFsNotificationBlocks can
* be processed concurrently.
* This lock ensures that the iServerTail is safe.
*/
//ToDo: This should be a ReadWriteLock
RFastLock iTailSemaphore;
/*
* The iClientSyncLock is a Read/Write style lock whereby it is
* set up with the value of KNotificationPoolSize.
* When a block is allocated it calls wait on this lock.
*
* This lock is to ensure that all of the currently processing blocks are
* written before the client is updated.
*
* i.e. if two blocks are being processed concurrently and the second
* block is written we need to wait until the first one is also written
* before the client receives the updated tail.
*/
//ToDo: This should be a ReadWriteLock
RFastLock iClientSyncLock;
/*
* HashMap<DriveNumber, TFsNotificationTypeDriveArray>
* HashMap<DriveNumber, RArray<TFsNotificationTypeArray>>
* HashMap<DriveNumber, RArray<RArray<TFsNotificationTypeFilter>>>
*
* Each value of iDrivesTypesFiltersMap is of type TFsNotificationTypeDriveArray
* associated with a particular drive.
*
* Each index of the TFsNotificationTypeDriveArray is a TFsNotificationTypeArray
*/
RHashMap<TInt,TFsNotificationTypeDriveArray> iDrivesTypesFiltersMap;
/*
* The iPathFilterList is an RPointerArray of CFsNotificationPathFilters.
*
* These are normally only accessed via a TFsNotificationTypeFilter (via iDrivesTypesFiltersMap),
* not via this array directly.
*/
RPointerArray<CFsNotificationPathFilter> iPathFilterList;
RMessage2 iBufferMsg; //To update buffer
RMessage2 iClientMsg; //client notification request
CSessionFs* iSession; //Session associated with this request (TFsSessionDisconnect::DoRequestL)
TNotifyRequestStatus iNotifyRequestStatus; //Current status of this request
//The following 3 variables must be aligned when modified.
TInt iClientHead; //Offset where the client should start reading from.
//If the server writes past this offset we must overflow the client.
TInt iClientTail; //The end of the client's accessible range.
TInt iServerTail; //The end of the server's accessible range.
//Overflow occurs if iServerTail becomes more than iClientHead.
TInt iClientBufferSize; //Buffer size is word-aligned.
friend class TFsNotificationBuffer; //For access to iClientBufferSize and iBufferMsg
friend class TFsNotificationRequest;//For access to iClientBufferSize
friend class FsNotificationManager; //For access to iSession
friend class TFsNotificationOpen; //For access to iSession
friend class TFsNotificationRemove; //For access to iDrivesTypesFiltersMap
};
/**
* A CFsNotificationBlock is a chunk of memory which is used to represent a notification
* such that a single IPC can be performed from server to client.
*
* CFsNotificationBlocks are stored in a CFsPool<CFsNotificationBlock>.
*
*@internalTechnology
*/
class CFsNotificationBlock
{
public:
static CFsNotificationBlock* New();
~CFsNotificationBlock();
TAny* Data();
private:
CFsNotificationBlock();
TText8 iData[KMinNotificationBufferSize];
};
/**
* Helper class to get certain attributes from or about a particular operation to used in a notification
*
* @internalTechnology
*/
class FsNotificationHelper
{
public:
static void NotificationType(TInt aFunction,TFsNotification::TFsNotificationType& aNotificationType);
static void PathName(CFsClientMessageRequest& aRequest, TDes& aName);
static void NewPathName(CFsClientMessageRequest& aRequest, TPtrC& aName);
static TInt NotificationSize(CFsClientMessageRequest& aRequest, TFsNotification::TFsNotificationType aNotificationType, const TDesC& aName);
static TInt TypeToIndex(TFsNotification::TFsNotificationType aType);
static TFsNotification::TFsNotificationType NotificationType(TInt& aIndex);
static TInt DriveNumber(const TPtrC& aPath);
static void Attributes(CFsClientMessageRequest& aRequest, TUint& aSet, TUint& aClear);
};
/**
* The FsNotificationManager is a static object
*
*@internalTechnology
*/
class FsNotificationManager
{
public:
//New notification request from client
static void AddNotificationRequestL(CFsNotifyRequest* aNotificationRequest);
//Notification request cancel
static void RemoveNotificationRequest(CFsNotifyRequest* aNotificationRequest);
//Notification request cancel (session closed)
static void RemoveNotificationRequest(CSessionFs* aSession);
/* A change has occurred represented by this request.
* Work out which CFsNotifyRequests are interested
* (if any) and call CFsNotifyRequest::NotifyChange.
*/
static void HandleChange(CFsClientMessageRequest& aRequest);
/* A change has occurred represented by this request.
* Work out which CFsNotifyRequests are interested
* (if any) and call CFsNotifyRequest::NotifyChange.
*
* This override is used directly when we want to force a particular notification type
*/
static void HandleChange(CFsClientMessageRequest& aRequest, TFsNotification::TFsNotificationType aType);
/*
* This override is used directly when we want to specify the current operation's name (src) and notification type.
*
* aRequest can be NULL when the request doesn't come from the file server
* such as when a media card is removed, see LocalDrives::CompleteDriveNotifications
*
* @See LocalDrives::CompleteDriveNotifications(TInt aDrive)
*/
static void HandleChange(CFsClientMessageRequest* aRequest, const TDesC& aOperationName, TFsNotification::TFsNotificationType aType);
//Initialise iNotifyRequests and iStaticNotification
static void OpenL();
static TBool IsInitialised();
/*
* On CFsNotifyRequest closing, Close is called if this is the last request being removed.
* This removes all of the managers private data.
*/
static void Close();
/*
* Calls SetFilterRegister for every valid notification set in aMask.
*/
static void SetFilterRegisterMask(TUint aMask,TBool aAdd);
/*
* Adds or Removes to the count of filters set up for a particular type
* This is a global count such that if there are no fiters for a particular type
* HandleChange doesn't need to do any iteration for that type.
*/
static void SetFilterRegister(TUint aFilter, TBool aAdd, TInt aCount = 1);
/*
* Get the number of registers filters set up on a particular type.
* @param aIndex the TFsNotificationType's index as determined from FsNotificationHelper::TypeToIndex
*/
static TInt& FilterRegister(TInt aIndex);
/*
* Returns the number of CFsNotifyRequests set up
*/
static TInt Count();
/*
* Lock the iChainLock (currently not a ReadWriteLock)
*/
static void Lock();
/*
* Unlock iChainLock
*/
static void Unlock();
private:
/*
* @internalTechnology
* Used by DoMatchFilter and DoHandleChange to control the flow of
* loop execution.
*/
enum TFsNotificationFilterMatch
{
EDifferent = 0x00, //Operation and Filters do not match.
EMatch = 0x01, //Operation and Filters do match.
EContinue = 0x02 //Data caged directory - Do not notify.
};
/*
* Checks whether aOperation matches the filter name and/or path set in aFilter.
*/
static TFsNotificationFilterMatch DoMatchFilter(CFsClientMessageRequest* aRequest, const TDesC& aOperationName,CFsNotificationPathFilter& aFilter);
/*
* Iterates filters for a particular drive.
* Called from HandleChange
*/
static void DoHandleChange(TFsNotificationTypeArray* aFilterTypeArray, TInt& aSeenFilter, CFsClientMessageRequest* aRequest, CFsNotifyRequest* aNotifyRequest, const TDesC& aOperationName, TFsNotification::TFsNotificationType& aType);
/*
* Stores the CFsNotifyRequests
*/
static CFsObjectCon* iNotifyRequests;
//As we are doing notifications 'in-place' which is multi-threaded
//we need to have locking to protect iNotifyRequests.
//ToDo: ReadWriteLock
static RFastLock iChainLock;
/*
* Global register per filter type.
* Keeps a count of the number of filters set up for a particular type
* (NB: EMediaChange is reported regardless of filters set)
*/
static TInt iFilterRegister[KNumRegisterableFilters];
/*
* This is a pool of blocks which are server-side versions of TFsNotification.
* They are used so that we can have a single IPC from server to client.
*
* it will also be used for coalescing changes.
*/
static CFsPool<CFsNotificationBlock>* iPool;
friend class CFsNotifyRequest;
friend class RequestAllocator;
friend class TFsNotificationSubClose;
};
#endif /* SF_NOTIFIER_H */