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