--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/contacts/symbian/contactsmodel/cntmodel/src/rcntmodel.cpp Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,2189 @@
+/*
+* Copyright (c) 2004-2009 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:
+*
+*/
+
+
+/**
+ @file
+ @internalComponent
+ @released
+*/
+
+#include <s32mem.h>
+#include <cntitem.h>
+#include <cntdb.h>
+#include <cntfilt.h>
+
+#include "rcntmodel.h"
+#include "persistencelayer.h" // for mlplcollection.
+#include "ccntnotificationmonitor.h"
+#include "ccontactprivate.h" // for mprogresseventhander.
+#include "ccntpackager.h"
+#include <cntviewstore.h>
+
+
+/** Contacts server version number. */
+const TInt KCntServerMajorVersionNumber=1;
+const TInt KCntServerMinorVersionNumber=1;
+const TInt KCntServerBuildVersionNumber=1;
+
+/** Number of attempts to try restart the server after premature termination. */
+const TInt KMaxTimesToRestartServer = 3;
+
+/** Maximum number of asynchronous IPC calls. */
+const TInt KAsyncMessageSlots=6;
+
+
+/**
+Unlock the last locked contact item on the server. Used by OpenLX(). If any
+method which locks a contact item leaves thenccnt this method is called as the
+cleanup stack unwinds, removing the lock for the last locked contact item.
+
+@capability None
+*/
+void CleanupUnlockRecord(TAny *aSession)
+ {
+ static_cast<RCntModel*>(aSession)->UnlockLastLockedContact();
+ }
+
+
+/**
+Unlock the last locked contact item on the server. Used by OpenLX().
+
+@capability None
+*/
+void RCntModel::UnlockLastLockedContact()
+ {
+ CloseContact(KNullContactId);
+ }
+
+
+/**
+Push a Contact item unlock cleanup item on the cleanup stack.
+*/
+void RCntModel::PushUnlockL() const
+ {
+ CleanupStack::PushL( TCleanupItem(CleanupUnlockRecord, const_cast<RCntModel *>(this)));
+ }
+
+
+/**
+RCntModel constructor.
+
+Member variables must be initialised (zero'd) in the constructor since
+RCntModel does not derive from CBase.
+*/
+RCntModel::RCntModel()
+ :
+ iDbNotifyMonitor(NULL),
+ iPackager(NULL),
+ iConnectionId(0),
+ iNoOfSvrStartAttempts(0)
+ {
+ }
+
+
+/**
+Get the Contacts server version number.
+
+@return Contacts server version number.
+*/
+TVersion RCntModel::Version() const
+ {
+ return(TVersion(KCntServerMajorVersionNumber,KCntServerMinorVersionNumber,KCntServerBuildVersionNumber));
+ }
+
+
+/** Name of the executable for the Contacts server. */
+_LIT(KCntServerExe,"CNTSRV.EXE");
+/** Name used to connect a session to the Contacts server. */
+_LIT(KCntServerName,"CNTSRV");
+
+
+/**
+Open a Contacts server session.
+*/
+void RCntModel::ConnectL()
+ {
+ // Assume the server is already running and attempt to create a session
+ // with a maximum of KAsyncMessageSlots message slots.
+ TInt err = CreateSession(KCntServerName,Version(),KAsyncMessageSlots);
+
+ if(err == KErrNotFound) // Server not running?
+ {
+ // Use the RProcess API to start the server.
+ RProcess server;
+ User::LeaveIfError(server.Create(KCntServerExe,KNullDesC));
+
+ //Enforce server to be at system default priority EPriorityForeground
+ //Contact server used to set as EPriorityHigh and this caused client could
+ //not get control or responsive until sorting by the idle sorter in remote
+ //view was done.
+ server.SetPriority(EPriorityForeground);
+
+ // Synchronise with the server.
+ TRequestStatus reqStatus;
+ server.Rendezvous(reqStatus);
+ server.Resume();
+
+ // Server will call the reciprocal static synchronisation call.
+ User::WaitForRequest(reqStatus);
+ server.Close();
+ User::LeaveIfError(reqStatus.Int());
+
+ // Create the server session.
+ User::LeaveIfError(CreateSession(KCntServerName,Version(),KAsyncMessageSlots));
+ }
+ else
+ {
+ User::LeaveIfError(err);
+ }
+
+ // Create object packer/unpacker if it doesn't already exist.
+ if (iPackager == NULL)
+ {
+ iPackager = CCntPackager::NewL();
+ }
+
+ // Each session (client) will be given a unique ID known as the Connection
+ // ID. This ID forms part of the database event notification message. This
+ // ID is created during the connection to the server.
+ ConnectionId();
+ }
+
+
+/**
+Close session.
+*/
+void RCntModel::Close()
+ {
+ delete iPackager;
+ iPackager = NULL;
+ delete iDbNotifyMonitor;
+ iDbNotifyMonitor = NULL;
+ RHandleBase::Close();
+ }
+
+
+/**
+Open a named contact database.
+
+Opens the default contacts database if the default argument is used. The given
+descriptor must not contain more than KCntMaxFilePath characters.
+
+@param aCntFile Filename (in the form drive:database). Defaults to
+KCntDefaultDrive.
+
+@return KErrNone if success, KErrArgument if the given descriptor contains more than the
+ maximum length of 190 characters, otherwise one of the System error codes.
+
+@capability ReadUserData
+*/
+TInt RCntModel::OpenDatabase(const TDesC& aCntFile) const
+ {
+ TInt err = SetFileName(aCntFile);
+ if (err == KErrNone)
+ {
+ TIpcArgs args;
+ args.Set(0,&aCntFile);
+ err = SendReceive(ECntOpenDataBase, args);
+ }
+
+ return err;
+ }
+
+
+/**
+Open a named contact database asynchronously.
+
+Opens the default contacts database if the default argument is used. The given
+descriptor must not contain more than KCntMaxFilePath characters.
+
+@param aStatus Asynchronous request object. Request is completed when database
+has been opened.
+@param aCntFile Contacts database filename (in the form drive:database).
+Defaults to KCntDefaultDrive.
+
+@capability ReadUserData
+*/
+void RCntModel::OpenDatabaseAsyncL(TRequestStatus& aStatus, const TDesC& aCntFile)
+ {
+ User::LeaveIfError(SetFileName(aCntFile));
+
+ TIpcArgs args;
+ args.Set(0,&aCntFile);
+ SendReceive(ECntOpenDataBase, args, aStatus);
+ }
+
+
+/**
+Cancel last asynchronous database open request.
+
+@capability None
+*/
+void RCntModel::CancelAsyncOpen()
+ {
+ SendReceive(ECntCancelAsyncOpenDatabase);
+ }
+
+
+/**
+Handle a premature termination of the contact server process by re-connecting
+the session and re-opening the database.
+
+@capability ReadUserData
+*/
+void RCntModel::HandlePrematureServerTerminationL()
+ {
+ if (iNoOfSvrStartAttempts > KMaxTimesToRestartServer)
+ {
+ User::Leave(KErrServerTerminated);
+ }
+ ConnectL();
+ User::LeaveIfError(OpenDatabase(iFileName));
+ ++iNoOfSvrStartAttempts;
+ }
+
+
+/**
+Set the database filename. Used to re-open the database if the server is
+terminated prematurely.
+
+@param aCntFile Contacts database filename (in the form drive:database).
+@return KErrNone if success, KErrArgument if the given descriptor contains more than the
+ maximum length of 190 characters.
+*/
+TInt RCntModel::SetFileName(const TDesC& aCntFile) const
+ {
+ if(aCntFile.Length() > KCntMaxFilePath)
+ {
+ return KErrArgument;
+ }
+
+ iFileName = aCntFile;
+ return KErrNone;
+ }
+
+
+/**
+Close currently open database.
+
+@capability None
+*/
+void RCntModel::CloseDatabase() const
+ {
+ (void)SendReceive(ECntCloseDataBase);
+ }
+
+
+/**
+Get the machine ID.
+
+Note: This function can leave.
+
+@return Machine ID.
+
+@leave KErrNone The send operation is successful
+@leave KErrServerTerminated The server no longer present
+@leave KErrServerBusy There are no message slots available
+@leave KErrNoMemory There is insufficient memory available
+
+
+@capability None
+*/
+TInt64 RCntModel::MachineId() const
+ {
+ TIpcArgs args;
+ TPckgBuf<TInt64> machineID;
+ args.Set(0, &machineID);
+ User::LeaveIfError(SendReceive(ECntMachineID, args)); // this can leave
+ return machineID();
+ }
+
+
+/**
+Set the machine ID (debug only).
+
+@param aMachineUniqueId New machine ID.
+
+@capability None
+*/
+void RCntModel::OverrideMachineUniqueId(TInt64 aMachineUniqueId)
+ {
+ TIpcArgs args;
+ TPckgBuf<TInt64> machineID(aMachineUniqueId);
+ args.Set(0, &machineID);
+ (void)SendReceive(ECntOverrideMachineID, args);
+ }
+
+
+/**
+Replace a named contact database with an empty one, replacing any database with
+the same name. Replaces the default contacts database if the default argument
+is used. The given descriptor must not contain more than KCntMaxFilePath
+characters.
+
+@param aCntFile Contacts database filename (in the form drive:database).
+Defaults to KCntDefaultDrive.
+
+@return KErrNone if success, KErrArgument if the given descriptor contains more than the
+ maximum length of 190 characters, KErrInUse if the database is currently open,
+ otherwise one of the System error codes.
+
+@capability WriteUserData
+*/
+TInt RCntModel::ReplaceDatabase(const TDesC& aCntFile) const
+ {
+ TInt err = SetFileName(aCntFile);
+ if (err == KErrNone)
+ {
+ TIpcArgs args;
+ args.Set(0,&aCntFile);
+
+ err = SendReceive(ECntReplaceDatabase, args);
+ }
+
+ return err;
+ }
+
+
+/**
+Returns an array of contact item IDs for all the contact items which may contain
+the specified telephone number in a telephone, fax or SMS type field.
+
+@see CContactDatabase::MatchPhoneNumberL() for more details on the match.
+
+@param aNumber Phone number string
+@param aMatchLengthFromRight Number of digits from the right of the phone number
+to use. Up to 15 digits can be specified and it is recommended that at least 7
+match digits are specified.
+
+@return CContactIdArray of candidate matches.
+
+@capability ReadUserData
+*/
+CContactIdArray* RCntModel::MatchPhoneNumberL(const TDesC& aNumber, const TInt aMatchLengthFromRight)
+ {
+ TIpcArgs args;
+ args.Set(0, &iPackager->GetReceivingBufferL());
+ args.Set(1, MLplCollection::EMatchPhoneNos);
+ args.Set(2, &aNumber);
+ args.Set(3, aMatchLengthFromRight);
+
+ TInt newBuffSize = 0;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetCollection, args));
+
+ if (newBuffSize > 0)
+ {
+ // If the buffer is not large enough resize the packager's internal
+ // buffer and make the call again.
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetCollection, args));
+ }
+
+ CContactIdArray* idArray = iPackager->UnpackCntIdArrayLC();
+ CleanupStack::Pop(idArray);
+
+ return idArray;
+ }
+
+
+
+/**
+Create an empty named contact database.
+
+Creates the default contacts database if the default argument is used. The
+given descriptor must not contain more than KCntMaxFilePath characters.
+
+@param aCntFile Contacts database filename (in the form drive:database).
+Defaults to KCntDefaultDrive.
+
+@return KErrNone if success, KErrArgument if the given descriptor contains more than the
+ maximum length of 190 characters, KErrAlreadyExists if the database already
+ exists otherwise one of the System error codes.
+
+@capability WriteUserData
+*/
+TInt RCntModel::CreateDatabase(const TDesC& aCntFile) const
+ {
+ TInt err = SetFileName(aCntFile);
+ if (err == KErrNone)
+ {
+ TIpcArgs args;
+ args.Set(0,&aCntFile);
+ err = SendReceive(ECntCreateDatabase, args);
+ }
+ return err;
+ }
+
+
+/**
+Create the system template.
+
+@return KErrNone if success otherwise one of the System error codes.
+
+@capability WriteUserData
+*/
+TInt RCntModel::ReCreateTemplate() const
+ {
+ return SendReceive(ECntReCreateTemplate);
+ }
+
+
+/**
+Get the database file UID.
+
+@return Pointer to file UID.
+
+@capability None
+*/
+TPtrC RCntModel::FileUidL() const
+ {
+ TIpcArgs args;
+ args.Set(0, &iFileUid);
+ User::LeaveIfError (SendReceive(ECntFileUniqueId, args));
+ return iFileUid;
+ }
+
+
+/**
+Determine if the database is ready. Ready in this context means that the
+database is open and readable/writable (i.e. the state machine is in
+CStateWritable).
+
+@capability None
+
+@return ETrue if the database is ready, EFalse if the database is not ready.
+*/
+TBool RCntModel::DatabaseReadyL() const
+ {
+ TBool retVal = EFalse;
+
+ // Although the ECntGetDatabaseReady msg. is completed with TBool values,
+ // during message processing, it is possible that ServiceError() occurs,
+ // causing the message to be completed with negative error codes.
+ // LeaveIfError() would protect against such cases.
+ User::LeaveIfError(retVal = SendReceive(ECntGetDatabaseReady));
+
+ return retVal;
+ }
+
+
+/**
+Tests whether a contact item's hint bit field matches a filter.
+
+@param aBitWiseFilter The filter to compare the item against. This is a
+combination of TContactViewFilter values.
+@param aContactId The ID of the item in the database.
+
+@return ETrue if the item is of the correct type for inclusion in the database
+and its hint bit field matches the specified filter, EFalse if either of these
+conditions are not met.
+
+@capability None
+*/
+TBool RCntModel::ContactMatchesHintFieldL(TInt aBitWiseFilter, TContactItemId aContactId)
+ {
+ TIpcArgs args;
+ args.Set(0, aBitWiseFilter);
+ args.Set(1, aContactId);
+ TBool retVal = EFalse;
+
+ // Although the ECntMatchesHintField msg. is completed with TBool values,
+ // during message processing, it is possible that ServiceError() occurs,
+ // causing the message to be completed with negative error codes.
+ // LeaveIfError() would protect against such cases.
+ User::LeaveIfError (retVal = SendReceive(ECntMatchesHintField, args));
+
+ return retVal;
+ }
+
+
+/**
+Delete a named contact database.
+
+Deletes the default contacts database if the default argument is used. The
+given descriptor must not contain more than KCntMaxFilePath characters.
+
+@param aCntFile Contacts database filename (in the form drive:database).
+Defaults to KCntDefaultDrive.
+
+@return KErrNone if success, KErrArgument if the given descriptor contains more
+ than the maximum length of 190 characters, KErrInUse if the database is
+ in use, KErrNotFound if the database does not exist otherwise one of the
+ System error codes.
+
+@capability WriteUserData
+*/
+TInt RCntModel::DeleteDatabase(const TDesC& aCntFile) const
+ {
+ if(aCntFile.Length() > KCntMaxFilePath)
+ {
+ return KErrArgument;
+ }
+ TIpcArgs args;
+ args.Set(0,&aCntFile);
+ return SendReceive(ECntDeleteDatabase,args);
+ }
+
+
+/**
+Get the name of the default contact database file.
+
+The given descriptor must have room for minimum of KCntMaxFilePath characters.
+
+@param aCntFile Contains the default contact database name on return.
+
+@return KErrNone if success, KErrArgument if the given descriptor contains more
+ than the maximum length of 190 characters, otherwise one of the System
+ error codes.
+
+@capability None
+*/
+TInt RCntModel::DefaultDatabase(TDes& aCntFile) const
+ {
+ TIpcArgs args;
+ args.Set(0,&aCntFile);
+ return SendReceive(ECntGetDefaultDatabaseName,args);
+ }
+
+
+/**
+Retrieve the current contact database drive.
+
+@param aDriveUnit Contains the current contact database drive unit on return.
+
+@return KErrNone if success otherwise one of the System error codes.
+
+@capability None
+*/
+TInt RCntModel::DatabaseDrive(TDriveUnit& aDriveUnit) const
+ {
+ TInt ret = SendReceive(ECntDatabaseDrive);
+ if (ret >= KErrNone)
+ {
+ aDriveUnit = ret;
+ }
+ return ret;
+ }
+
+
+/**
+Sets the contact database drive and optionally moves the default contact
+database from its current location to the new drive. This function guarantees
+an all or nothing operation. If the database is not successfully moved, the
+drive setting is not updated to reflect the change.
+
+@param aDriveUnit The drive to which to move the contact database.
+@param aCopy ETrue moves the existing file to the specified drive. Deletion of
+the source file will fail if it is in use. EFalse does not move the file. The
+default argument value is ETrue.
+
+@return KErrNone if success, KErrInUse if destination file for copy is open,
+KErrNotFound found if source contact database (default) cannot be found,
+KErrAlreadyExists if the destination contact database file exists.
+
+@capability WriteUserData
+*/
+void RCntModel::SetDatabaseDriveL(TDriveUnit aDriveUnit, TBool aCopy)
+ {
+ TIpcArgs args;
+ args.Set(0,aDriveUnit);
+ args.Set(1,aCopy);
+ User::LeaveIfError(SendReceive(ECntSetDatabaseDrive,args));
+ }
+
+/**
+Gets the size of the database file in bytes.
+
+@return The size of the contact database in bytes.
+
+@capability None
+*/
+TInt RCntModel::FileSize() const
+ {
+ return SendReceive(ECntFilesSize);
+ }
+
+/**
+Determine if the named contact database file exists.
+
+If the aCntFile argument is the default, determines if default contact database
+file exists.
+
+@param aCntFile Contacts database filename (in the form drive:database).
+@leave KErrArgument if the given descriptor contains more than the maximum length
+ of 190 characters, otherwise one of the System error codes.
+@return ETrue if exists, EFalse does not exist.
+@capability None
+*/
+TBool RCntModel::DatabaseExistsL(const TDesC& aCntFile) const
+ {
+ if(aCntFile.Length() > KCntMaxFilePath)
+ {
+ User::Leave(KErrArgument);
+ }
+
+ TIpcArgs args;
+ args.Set(0,&aCntFile);
+ TBool ret = EFalse;
+
+ // Although ECntDatabaseExists is normally completed with TBool values,
+ // ServiceError() can occur which leads to a negative system error codes
+ // being returned. User::LeaveIfError will allow that to be properly handled.
+ User::LeaveIfError(ret = SendReceive(ECntDatabaseExists,args));
+
+ return ret;
+ }
+
+
+/**
+List the contact database files for any given drive unit. Lists all databases
+on all drives if aDriveUnit is set to NULL.
+
+@param aDriveUnit The drive unit to search. Searches all drives if default
+argument is used.
+
+@return CDesCArray containing the database list. Always returns list even if
+empty.
+
+@leave KErrNoMemory if not enough free memory, KErrNotReady if drive does not,
+otherwise one of the System error codes.
+
+@capability ReadUserData
+*/
+CDesCArray* RCntModel::ListDatabasesL(TDriveUnit* aDriveUnit) const
+ {
+ // Convert the drive unit to an integer for IPC. The TDriveUnit constructor
+ // does not allow values outside 0 - 25.
+ TInt driveNumber;
+ (aDriveUnit == NULL) ? (driveNumber = ECntAllDrives) : (driveNumber = *aDriveUnit);
+
+ // Provide a CBufFlat for receiving the list of databases from the server.
+ // Format is a (proprietary) serialized CDesCArray.
+ const TInt KInitialBufferSize = 200;
+ CBufFlat* listBuffer = CBufFlat::NewL(KInitialBufferSize);
+ CleanupStack::PushL(listBuffer);
+ listBuffer->ExpandL(0,KInitialBufferSize);
+
+ // IPC argument list is:
+ // 0 (Return param) - Address of our receive buffer
+ // 1 (Param) - The drive number 0 - 25 or ECntAllDrives
+ // 2 (Return param) - The size of buffer required for the transfer
+ TIpcArgs args;
+ TPtr8 ptr(listBuffer->Ptr(0));
+ args.Set(0,&ptr);
+ args.Set(1,driveNumber);
+ TPckgBuf<TInt> size;
+ args.Set(2,&size);
+ User::LeaveIfError(SendReceive(ECntListDatabases,args));
+
+ // Check if our buffer was large enough by reading returned size argument.
+ // The server will not have written any data if our povided buffer was not
+ // big enough.
+ if(size() > KInitialBufferSize)
+ {
+ // Not big enough but now we know how big the buffer needs to be.
+ // Just allocate buffer again, reset the IPC args and call again.
+ CleanupStack::PopAndDestroy(listBuffer);
+ listBuffer = CBufFlat::NewL(size());
+ CleanupStack::PushL(listBuffer);
+ listBuffer->ExpandL(0,size());
+ ptr.Set(listBuffer->Ptr(0));
+ args.Set(0,&ptr);
+ args.Set(1,driveNumber);
+ args.Set(2,&size);
+ User::LeaveIfError(SendReceive(ECntListDatabases,args));
+ }
+
+ // Wrap the buffer in a read stream class to internalize.
+ RBufReadStream readStream;
+ readStream.Open(*listBuffer);
+ CleanupClosePushL(readStream);
+
+ // Number of array elements.
+ const TInt count = readStream.ReadUint32L();
+ // Use count if it is not zero.
+ CDesCArray* list = new(ELeave) CDesCArrayFlat(count ? count : 1);
+ CleanupStack::PushL(list);
+ for (TInt i=0; i<count; ++i)
+ {
+ TFileName path;
+ TInt length=readStream.ReadInt8L();
+ readStream.ReadL(path,length);
+ // Add to the list.
+ list->AppendL(path);
+ }
+
+ CleanupStack::Pop(list);
+ CleanupStack::PopAndDestroy(&readStream);
+ CleanupStack::PopAndDestroy(listBuffer);
+
+ return list;
+ }
+
+
+/**
+Add a new contact to the database.
+
+@param aContact The contact to be added to the database.
+
+@capability WriteUserData
+*/
+TContactItemId RCntModel::CreateContactL(CContactItem& aContact) const
+ {
+ // Pack the contact into the first IPC argument.
+ TIpcArgs args;
+ TPtr8 ptr(iPackager->PackL(aContact));
+ args.Set(0,&ptr);
+
+ TInt cntId = 0;
+ User::LeaveIfError(cntId = SendReceive(ECntItemCreate, args));
+
+ return cntId;
+ }
+
+
+/**
+Delete a contact from the database.
+
+@param aContact The contact to be deleted.
+@param aCntEventType The event type to pass on to the observers.
+@param aDecAccessCount If ETrue access count should be decremented prior to the
+deletion.
+
+@capability WriteUserData
+*/
+void RCntModel::DeleteContactL(TContactItemId aCntId, TCntSendEventAction aCntEventType, TBool aDecAccessCount) const
+ {
+ TIpcArgs args(aCntId, aCntEventType, aDecAccessCount);
+ User::LeaveIfError(SendReceive(ECntItemDelete, args));
+ }
+
+/**
+Open the database tables.
+
+@capability ReadUserData
+*/
+void RCntModel::OpenTablesL()
+ {
+ User::LeaveIfError(SendReceive(ECntReOpenDbTables));
+ }
+
+
+/**
+Close the database tables.
+
+@capability None
+*/
+void RCntModel::CloseTablesL()
+ {
+ User::LeaveIfError(SendReceive(ECntCloseDbTables));
+ }
+
+
+/**
+Update and unlock an existing contact in the database.
+
+@param aContact The contact to be updated and unlocked
+@param aSendChangedEvent If ETrue the changed event should be sent after the
+update/unlock.
+
+@capability WriteUserData
+*/
+void RCntModel::CommitContactL(const CContactItem& aContact, TBool aSendChangedEvent) const
+ {
+ //check what contacts were added/removed to a group
+ if (aContact.Type() == KUidContactGroup)
+ {
+ const CContactGroup& group = static_cast<const CContactGroup&>(aContact);
+ if (group.iInitialContactIds != NULL)
+ {
+ group.iAddedContactIds = CContactIdArray::NewL();
+ group.iRemovedContactIds = CContactIdArray::NewL();
+ for (int i = 0; i < group.iItems->Count(); i++)
+ {
+ if (group.iInitialContactIds->Find(group.iItems->operator[](i)) == KErrNotFound)
+ {
+ group.iAddedContactIds->AddL(group.iItems->operator[](i));
+ }
+ }
+ for (int j = 0; j < group.iInitialContactIds->Count(); j++)
+ {
+ if (group.iItems->Find(group.iInitialContactIds->operator[](j)) == KErrNotFound)
+ {
+ group.iRemovedContactIds->AddL(group.iInitialContactIds->operator[](j));
+ }
+ }
+ }
+ }
+
+ // Pack the contact into the first IPC argument.
+ TIpcArgs args;
+ TPtr8 ptr(iPackager->PackL((const_cast<CContactItem&>(aContact))));
+ args.Set(0,&ptr);
+
+ args.Set(1,aSendChangedEvent);
+
+ User::LeaveIfError(SendReceive(ECntItemCommit, args));
+
+ //clear list of contacts added/removed to a group
+ if (aContact.Type() == KUidContactGroup)
+ {
+ const CContactGroup& group = static_cast<const CContactGroup&>(aContact);
+ if (group.iAddedContactIds != NULL)
+ {
+ delete group.iAddedContactIds;
+ group.iAddedContactIds = NULL;
+ }
+ if (group.iRemovedContactIds != NULL)
+ {
+ delete group.iRemovedContactIds;
+ group.iRemovedContactIds = NULL;
+ }
+ if (group.iInitialContactIds != NULL)
+ {
+ delete group.iInitialContactIds;
+ group.iInitialContactIds = NULL;
+ }
+ }
+ }
+
+
+/**
+Unpacks a contact item contained within the Packager's internal buffer and
+creates a contact item object. Within the server, the contact item has been
+written to the Packager's internal buffer by the RMessage2.
+
+If the buffer size was returned then the buffer was not large enough to hold a
+serialised contact item. The buffer must be expanded to the size returned by
+the server (ie aBufferSize) and the read operation performed again.
+
+@see CCntPackager::SetBufferFromMessageL()
+
+@param aBufferSize The new buffer size.
+@param aArgs The argument buffer where the serialised contact item is written by
+the server.
+
+@return CContactItem unpacked from Packager's internal buffer.
+
+@capability ReadUserData
+*/
+CContactItem* RCntModel::UnPackContactLC(TInt aBufferSize, TIpcArgs& aArgs) const
+ {
+ if(aBufferSize > 0) // Packager's internal buffer is not large enough.
+ {
+ // Set new extended receiving buffer.
+ aArgs.Set(1, &iPackager->GetReceivingBufferL(aBufferSize));
+ // Perform read operation again.
+ User::LeaveIfError(SendReceive(ECntItemRead, aArgs));
+ }
+ return iPackager->UnpackCntItemLC();
+ }
+
+
+/**
+Read an existing contact in the database.
+
+@param aCntItemVDef The view definition to use.
+@param aCntId Contact ID to read.
+
+@return CContactItem object with contact ID aCntId.
+*/
+CContactItem* RCntModel::ReadContactL(const CContactItemViewDef* aCntItemVDef, TContactItemId aCntId) const
+ {
+ TIpcArgs args;
+
+ if (aCntItemVDef)
+ {
+ iPackager->PackL(*(const_cast<CContactItemViewDef*>(aCntItemVDef)));
+ // Indicates that a user ContactItemViewDef is present in first IPC
+ // argument.
+ args.Set(3,TRUE);
+ }
+ else
+ {
+ iPackager->Clear();
+ // Indicates that no user ContactItemViewDef is present in first IPC
+ // argument. The default view definition will be used instead.
+ args.Set(3,FALSE);
+ }
+
+ args.Set(0, &iPackager->GetTransmittingBuffer());
+ args.Set(1, &iPackager->GetReceivingBufferL());
+ args.Set(2, aCntId);
+
+ TInt newBuffSize = 0;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntItemRead, args));
+ CContactItem* cntItem = UnPackContactLC(newBuffSize, args);
+ CleanupStack::Pop(cntItem);
+
+ return cntItem;
+ }
+
+
+/**
+Open and lock an existing contact in the database. The contact item lock is
+pushed onto the cleanup stack.
+
+@param aCntItemVDef The view definition to use.
+@param aCntId Contact ID to read.
+
+@return Opened/locked CContactItem object.
+*/
+CContactItem* RCntModel::OpenContactLX(const CContactItemViewDef* aCntItemVDef, TContactItemId aCntId) const
+ {
+ TIpcArgs args;
+ if (aCntItemVDef)
+ {
+ iPackager->PackL(*(const_cast<CContactItemViewDef*>(aCntItemVDef)));
+ // Indicates that a user ContactItemViewDef is present in first IPC
+ // argument.
+ args.Set(3,TRUE);
+ }
+ else
+ {
+ iPackager->Clear();
+ // Indicates that no user ContactItemViewDef is present in first IPC
+ // argument. The default view definition will be used instead.
+ args.Set(3,FALSE);
+ }
+
+ args.Set(0, &iPackager->GetTransmittingBuffer());
+ args.Set(1, &iPackager->GetReceivingBufferL());
+ args.Set(2, aCntId);
+
+ TInt newBuffSize = 0;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntItemOpen, args));
+
+ // Push the contact item lock onto the cleanup stack. If UnPackContactLC()
+ // (or any other RCntModel method leaves after this point) then the last
+ // contact to be locked will be unlocked (aka closed). The method returns
+ // with the lock remaining on the cleanup stack.
+ PushUnlockL();
+
+ CContactItem* cntItem = UnPackContactLC(newBuffSize, args);
+
+ //save a list of contacts belonging to a group
+ if (cntItem->Type() == KUidContactGroup)
+ {
+ CContactGroup* group = static_cast<CContactGroup*>(cntItem);
+ delete group->iInitialContactIds;
+ group->iInitialContactIds = NULL;
+ group->iInitialContactIds = CContactIdArray::NewL(group->ItemsContained());
+ }
+
+ CleanupStack::Pop(cntItem);
+
+ return cntItem;
+ }
+
+
+/**
+Close/unLock an existing contact in the database.
+
+@param aCntId The contact ID of the contact to close.
+
+@return ETrue if the contact was opened before and closed after the call,
+EFalse if there was no need to close the contact.
+*/
+TBool RCntModel::CloseContact(TContactItemId aCntId)
+ {
+ TInt res = SendReceive(ECntItemClose, TIpcArgs(aCntId));
+ if (res == KErrNone)
+ {
+ return ETrue;
+ }
+ return EFalse;
+ }
+
+
+/**
+Start a database transaction.
+*/
+TInt RCntModel::BeginDbTransaction() const
+ {
+ return SendReceive(EBeginDbTransaction);
+ }
+
+
+/**
+Commit a database transaction.
+*/
+TInt RCntModel::CommitDbTransaction() const
+ {
+ return SendReceive(EEndDbTransaction);
+ }
+
+
+/**
+Rollback a database transaction.
+*/
+TInt RCntModel::RollbackDbTransaction() const
+ {
+ return SendReceive(ERollbackDbTransaction);
+ }
+
+
+/**
+Set the operation timeout value. This value is the length of time that a
+deferred request will remain in the request queue in the State Machine before
+being completed with an error code.
+
+@param aMicroSeconds The operation timeout in microseconds. This timeout will
+only be applied to requests sent by clients of this database after this point in
+time.
+*/
+void RCntModel::SetOperationTimeOutL(const TInt aMicroSeconds) const
+ {
+ TIpcArgs args;
+ args.Set(0, aMicroSeconds);
+ User::LeaveIfError(SendReceive(ECntOpsTimeOut, args));
+ }
+
+
+/**
+Change the existing contact view definition on the server.
+
+@param aView The new contact view defintion.
+*/
+void RCntModel::SetViewDefinitionL(const CContactViewDef& aView)
+ {
+ TPtr8 ptr(iPackager->PackL(aView));
+ User::LeaveIfError(SendReceive(ECntChangeViewDef, TIpcArgs(&ptr)));
+ }
+
+
+/**
+Add a database event observer.
+
+@param aObserver Observer callback inteface used to send database event.
+*/
+void RCntModel::AddObserverL(MContactDbObserver& aObserver)
+ {
+ // If this is the first observer to be added then required to create the
+ // database event notification monitor (lazy initialisation).
+ if (!iDbNotifyMonitor)
+ {
+ iDbNotifyMonitor = CCntDbNotifyMonitor::NewL(*this);
+ }
+ iDbNotifyMonitor->AddObserverL(aObserver);
+ }
+
+
+/**
+Remove a database event observer.
+
+@param aObserver Observer callback inteface to be removed.
+*/
+void RCntModel::RemoveObserver(const MContactDbObserver& aObserver)
+ {
+ if (iDbNotifyMonitor)
+ {
+ iDbNotifyMonitor->RemoveObserver(aObserver);
+ // If this is the last observer to be removed then dispose of the
+ // database event notification monitor (no longer required).
+ if (iDbNotifyMonitor->NumberOfListeners() == 0)
+ {
+ delete iDbNotifyMonitor;
+ iDbNotifyMonitor = NULL;
+ }
+ }
+ }
+
+/**
+Add a database event observer.
+
+@param aObserver Observer callback inteface used to send database event.
+*/
+void RCntModel::AddObserverV2L(MContactDbObserverV2& aObserver)
+ {
+ // If this is the first observer to be added then required to create the
+ // database event notification monitor (lazy initialisation).
+ if (!iDbNotifyMonitor)
+ {
+ iDbNotifyMonitor = CCntDbNotifyMonitor::NewL(*this);
+ }
+ iDbNotifyMonitor->AddObserverV2L(aObserver);
+ }
+
+
+/**
+Remove a database event observer.
+
+@param aObserver Observer callback inteface to be removed.
+*/
+void RCntModel::RemoveObserverV2(const MContactDbObserverV2& aObserver)
+ {
+ if (iDbNotifyMonitor)
+ {
+ iDbNotifyMonitor->RemoveObserverV2(aObserver);
+ // If this is the last observer to be removed then dispose of the
+ // database event notification monitor (no longer required).
+ if (iDbNotifyMonitor->NumberOfListeners() == 0)
+ {
+ delete iDbNotifyMonitor;
+ iDbNotifyMonitor = NULL;
+ }
+ }
+ }
+
+/**
+Request a database event from the server.
+
+@param aStatus Completed when database event is available.
+@param aEvent When aStatus is completed contains the database event.
+*/
+void RCntModel::StartNotificationTransfer(TRequestStatus& aStatus, TDes8& aEvent)
+ {
+ SendReceive(ECntRequestEvent, TIpcArgs(&aEvent), aStatus);
+ }
+
+
+/**
+End (cancel) request for database event from server.
+*/
+void RCntModel::EndNotificationTransfer()
+ {
+ SendReceive(ECntCancelEventRequest);
+ }
+
+/**
+Sets the ID of the current item and persists it in the database. The current
+item is provided for use by clients who want to identify one contact item in the
+database as the currently selected item.
+
+@param aContactId The ID of the current item.
+
+@capability WriteUserData
+*/
+void RCntModel::SetCurrentItem(TContactItemId aContactId) const
+ {
+ TIpcArgs args(aContactId);
+ (void)SendReceive(ECntSetCurrentItem, args);
+ }
+
+
+/**
+Gets the ID of the current item, as set by SetCurrentItem(). The current item
+ID is initialised to KNullContactId when the database is opened.
+
+@return The ID of the current item.
+
+@capability None
+*/
+TContactItemId RCntModel::CurrentItem() const
+ {
+ TContactItemId id = SendReceive(ECntGetCurrentItem);
+ if (id < 0)
+ {
+ return KNullContactId;
+ }
+ else
+ {
+ return id;
+ }
+ }
+
+
+/**
+When there are multiple contact databases on a device this method can be used to
+enquire which database is the current one.
+
+The current database functions are provided as part of current item
+functionality. In order to pass a current item from one contacts model client
+to another, the receiving client needs to be using the same database.
+
+The current database is a path and filename, set using SetCurrentDatabase(),
+which is persisted by the contacts server.
+
+@deprecated
+
+@param aDatabase The path and filename of the current database. KNullDesC
+if no current database has been set.
+
+@return KErrNone if success otherwise one of the System error codes.
+
+@capability None
+*/
+TInt RCntModel::GetCurrentDatabase(TDes& aDatabase) const
+ {
+ TIpcArgs args(&aDatabase);
+ return SendReceive(ECntGetCurrentDb, args);
+ }
+
+
+/**
+When there are multiple contact databases on a device this method can be used to
+set a database as the current one.
+
+Note: this function simply updates a file name which is stored by the Contacts
+server and its use is optional. It is provided as part of current item
+functionality.
+
+In order to pass a current item from one contacts model client to another,
+the receiving client needs to be using the same database.
+
+@deprecated
+
+@param aDatabase The path and filename of the current database.
+
+@return KErrNone if success otherwise one of the System error codes.
+
+@capability WriteUserData
+*/
+TInt RCntModel::SetCurrentDatabase(const TDesC& aDatabase) const
+ {
+ TIpcArgs args(&aDatabase);
+ return SendReceive(ECntSetCurrentDb, args);
+ }
+
+
+/**
+Returns the ID of the contact item whose telephone number field is mapped to
+the speed dial position specified.
+
+This function is provided so that information may be displayed about a contact
+item whose telephone number is being dialled using speed dialling.
+
+The function also retrieves the telephone number stored in the field.
+
+@param aSpeedDialPosition The speed dial position. This is an integer in the
+range 1 to 9 inclusive. If outside this range, the function leaves with
+KErrArgument.
+@param aPhoneNumber On return contains the telephone number which is mapped to
+the speed dial position specified. Returns KNullDesC if the speed dial position
+requested has not been set.
+
+@return The ID of the contact item to which the speed dial is mapped.
+
+@capability ReadUserData
+*/
+TContactItemId RCntModel::GetSpeedDialFieldL(TInt aSpeedDialPosition, TDes& aPhoneNumber)
+ {
+ __ASSERT_ALWAYS(aSpeedDialPosition >= KCntMinSpeedDialIndex && aSpeedDialPosition <= KCntMaxSpeedDialIndex , User::Leave(KErrArgument));
+ TPckgBuf<TContactItemId> contact(KNullContactId);
+ TIpcArgs args(aSpeedDialPosition,&aPhoneNumber);
+ return (TContactItemId)SendReceive(ECntGetSpeedDialContactIdAndPhoneNumber, args);
+ }
+
+
+/**
+Sets a field containing a telephone number as a speed dial field. The field
+is identified by aFieldIndex within the contact item aItem. It is assigned a
+speed dial position between 1 and 9 inclusive.
+
+The field's speed dial and user added attributes are set and the appropriate
+UID (KUidSpeedDialXxx) is added to the field's content type. The changes are
+committed to the database.
+
+Notes:
+
+- If an item's telephone number field has already been assigned to position
+aSpeedDialPosition, that item is updated so that the speed dial attribute
+is removed from its field and the speed dial field type UID is removed from
+the field's content type, before the new speed dial field is set.
+
+- The speed dial attribute can be tested for using the
+CContactItemField::IsSpeedDial() method.
+
+- The contact item passed to this function (aItem) must have been obtained using
+one of the variants of CContactDatabase::OpenContactL(). This is because it
+is modified and committed to the database by this function - no further
+commits are necessary.
+
+@param aItem The contact item containing the field to set as a speed dial
+field.
+@param aFieldIndex Index of a field in aItem's field set to set as a speed dial
+field.
+@param aSpeedDialPosition The speed dial position. This is an integer in the
+range 1 to 9 inclusive. If outside this range, the function leaves with
+KErrArgument.
+
+@leave KErrDiskFull The disk does not have enough free space to perform the
+operation.
+
+@capability ReadUserData
+@capability WriteUserData
+*/
+void RCntModel::SetFieldAsSpeedDialL(TContactItemId aContactId, TInt aFieldIndex, TInt aSpeedDialPosition)
+ {
+ __ASSERT_ALWAYS(aSpeedDialPosition >= KCntMinSpeedDialIndex && aSpeedDialPosition <= KCntMaxSpeedDialIndex , User::Leave(KErrArgument));
+ TIpcArgs args(aSpeedDialPosition, aContactId, aFieldIndex);
+ User::LeaveIfError(SendReceive(ECntSetSpeedDialIdForPosition, args));
+ }
+
+
+/**
+Removes the mapping between a contact item field and a speed dial position.
+
+Removes the KUidSpeedDialXxx UID from the field's content type, removes the
+field's speed dial attribute and commits the changes to the item.
+
+@param aContactId The ID of the contact item containing the speed dial field.
+@param aSpeedDialPosition The speed dial position. This is an integer in the
+range 1 to 9 inclusive. If outside this range, the function leaves with
+KErrArgument.
+
+@leave KErrDiskFull The disk does not have enough free space to perform the operation.
+
+@capability ReadUserData
+@capability WriteUserData
+*/
+void RCntModel::RemoveSpeedDialFieldL(TContactItemId aContactId, TInt aSpeedDialPosition)
+ {
+ __ASSERT_ALWAYS(aSpeedDialPosition >= KCntMinSpeedDialIndex && aSpeedDialPosition <= KCntMaxSpeedDialIndex , User::Leave(KErrArgument));
+ // Tell the server that this speed dial slot is now free. Third argument
+ // (i.e. field index) is not used.
+ TIpcArgs args(aSpeedDialPosition, aContactId, -1);
+ User::LeaveIfError(SendReceive(ECntSetSpeedDialIdForPosition, args));
+ }
+
+
+/**
+Returns the ID of the template that should be used with CContactICCEntry items.
+If aPhonebookUid is set, the ID of the template belonging to the phonebook with
+TUid aPhonebookUid is returned.
+
+@param aPhonebookUid The phonebook ID with default KNullUid.
+
+@return A template ID.
+*/
+TContactItemId RCntModel::ICCTemplateIdL(TUid aPhonebookUid)
+ {
+ TIpcArgs args(aPhonebookUid.iUid);
+ return SendReceive(ECntICCTemplateId, args);
+ }
+
+
+/**
+Returns the ID of the contacts model group which represents the ADN phonebook.
+
+@return ADN phonebook group ID.
+*/
+TContactItemId RCntModel::PhonebookGroupIdL()
+ {
+ return SendReceive(ECntPhonebookGroupId);
+ }
+
+
+/**
+Sets an existing contact item to be the database's current own card.
+
+@param aContact The contact item to set as the database's current own card.
+It must already exist in the database. It cannot be a group or a template.
+
+@leave KErrNotFound aContact does not exist in the database.
+@leave KErrDiskFull The disk does not have enough free space to perform the
+operation.
+
+@capability None
+*/
+void RCntModel::SetOwnCardL(const CContactItem& aContact)
+ {
+ TIpcArgs args;
+ TPtr8 ptr(iPackager->PackL(aContact));
+ args.Set(0,&ptr);
+ User::LeaveIfError(SendReceive(ECntSetOwnCard, args));
+ }
+
+
+/**
+Returns the ID of the database's current own card.
+
+Having obtained this ID, the client may then open the own card in the same
+way as an ordinary contact card (using ReadContactL() or OpenContactL()).
+
+@return The ID of the database's current own card. KNullContactId if the own
+card has been deleted or has not yet been set.
+*/
+TContactItemId RCntModel::OwnCard() const
+ {
+ return static_cast<TContactItemId>(SendReceive(ECntGetOwnCard));
+ }
+
+
+/**
+Returns the connection ID of the current client session.
+
+@return Connection ID of the current client session.
+*/
+TInt RCntModel::ConnectionId() const
+ {
+ // If asking for the connection ID the first time, then we ask the server
+ // about the session id (lazy initialisation).
+ if (!iConnectionId)
+ {
+ iConnectionId = SendReceive(ECntConnectionId);
+ }
+ return iConnectionId;
+ }
+
+
+/**
+Debug only.
+
+@capability None
+*/
+void RCntModel::SetHeapFailure(RHeap::TAllocFail aType, TInt aRate)
+ {
+ TIpcArgs args(aType,aRate);
+ SendReceive(ECntSetHeapFailure,args);
+ }
+
+
+/**
+Debug only.
+
+@return The heap size usage of the server in debug mode, 0 in release mode.
+
+@capability None
+*/
+TInt RCntModel::ResourceCount()
+ {
+ return(SendReceive(ECntResourceCount));
+ }
+
+
+/**
+Returns the ID of the database's preferred template, as set by the method
+SetPrefTemplateL(). The preferred template is for clients who may have
+multiple templates but want to identify one as preferred.
+
+@return The ID of the database's current preferred template. KNullContactId if
+not set.
+*/
+TContactItemId RCntModel::PrefTemplateId() const
+ {
+ return(SendReceive(ECntGetPrefTemplateId));
+ }
+
+
+/**
+Sets the database's preferred template.
+
+The preferred template's ID persists when the database is opened and closed.
+If the preferred template is subsequently deleted, the preferred template
+ID is set to KNullContactId.
+
+@param aContact The contact card template to set as the database's preferred
+template.
+
+@leave KErrNotSupported The item is not a template (i.e. is not of type
+KUidContactCardTemplate).
+@leave KErrDiskFull The disk does not have enough free space to perform the
+operation.
+
+@capability WriteUserData
+*/
+void RCntModel::SetPrefTemplateL(const TContactItemId aContactId)
+ {
+ TIpcArgs args(aContactId);
+ User::LeaveIfError(SendReceive(ECntSetPrefTemplateId, args));
+ }
+
+
+/**
+Get a list of template IDs from the server.
+
+@return Array of template IDs.
+*/
+CContactIdArray* RCntModel::FetchTemplateListIdsL()
+{
+ TIpcArgs args;
+ args.Set(0, &iPackager->GetReceivingBufferL());
+
+ TInt newBuffSize;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntFetchTemplateIds, args));
+ if (newBuffSize > 0)
+ {
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntFetchTemplateIds, args));
+ }
+
+ CContactIdArray* idArray = iPackager->UnpackCntIdArrayLC();
+ CleanupStack::Pop(idArray);
+
+ return idArray;
+}
+
+
+/**
+Get a list of group IDs from the server.
+
+@return Array of group IDs.
+*/
+CContactIdArray* RCntModel::FetchGroupListIdsL()
+{
+ TIpcArgs args;
+ args.Set(0, &iPackager->GetReceivingBufferL());
+
+ TInt newBuffSize;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntFetchGroupIdLists, args));
+ if (newBuffSize > 0)
+ {
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntFetchGroupIdLists, args));
+ }
+
+ CContactIdArray* idArray = iPackager->UnpackCntIdArrayLC();
+ CleanupStack::Pop(idArray);
+
+ return idArray;
+}
+
+/**
+Get a collection of contact IDs using the specified collection parameters.
+
+@param aCollectionType Type of collection.
+@param aTime Used if collection type is "changed since".
+@param aGuid Used if collection type is "find GUID".
+
+@return Contact ID array of those contacts which match the specified collection
+parameters.
+*/
+CContactIdArray* RCntModel::CollectionL(TInt aCollectionType, TTime aTime,const TDesC& aGuid)
+ {
+ CContactIdArray* idArray = NULL;
+ switch(aCollectionType)
+ {
+ case MLplCollection::EChangedSince :
+ {
+ idArray = DoGetCollectionChangedSinceL(aTime);
+ }
+ break;
+ case MLplCollection::EDeleted :
+ case MLplCollection::EUnfiled :
+ {
+ idArray = DoGetCollectionL(aCollectionType);
+ }
+ break;
+ case MLplCollection::EFindGuid :
+ {
+ idArray = DoGetCollectionGuidL(aGuid);
+ }
+ break;
+ default:
+ {
+ User::Leave(KErrNotFound);
+ }
+ break;
+ }
+ return idArray;
+ }
+
+
+/**
+Get a collection of contact IDs which have changed since the given time.
+
+@param aTime Changed since time.
+
+@return Contact ID array of those contacts which have changed since the given
+time.
+*/
+CContactIdArray* RCntModel::DoGetCollectionChangedSinceL(TTime aTime)
+ {
+ TIpcArgs args;
+ args.Set(0, &iPackager->GetReceivingBufferL());
+ args.Set(1,MLplCollection::EChangedSince);
+ TPckgBuf<TTime> pckg(aTime);
+ args.Set(2,&pckg);
+
+ TInt newBuffSize;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetCollection, args));
+ if (newBuffSize > 0)
+ {
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetCollection, args));
+ }
+
+ CContactIdArray* idArray = iPackager->UnpackCntIdArrayLC();
+ CleanupStack::Pop(idArray);
+
+ return idArray;
+ }
+
+
+/**
+Get a collection of contact IDs for the given collection type.
+
+@param aCollectionType Collection type.
+
+@return Contact ID array of those contacts which meet the given collection type
+criteria.
+*/
+CContactIdArray* RCntModel::DoGetCollectionL(TInt aCollectionType)
+ {
+ TIpcArgs args;
+ args.Set(0, &iPackager->GetReceivingBufferL());
+ args.Set(1,aCollectionType);
+
+ TInt newBuffSize;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetCollection, args));
+ if (newBuffSize > 0)
+ {
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetCollection, args));
+ }
+
+ CContactIdArray* idArray = iPackager->UnpackCntIdArrayLC();
+ CleanupStack::Pop(idArray);
+
+ return idArray;
+ }
+
+
+/**
+Get a collection of contact IDs which have the given GUID. Since GUIDs are
+unique (i.e. no two contacts can have the same GUID) there will only ever be
+one contact ID in this collection.
+
+@param aGuid Contact GUID.
+
+@return Contact ID array containing the contact with the given GUID.
+*/
+CContactIdArray* RCntModel::DoGetCollectionGuidL(const TDesC& aGuid)
+ {
+ TIpcArgs args;
+ args.Set(0, &iPackager->GetReceivingBufferL());
+ args.Set(1,MLplCollection::EFindGuid);
+ args.Set(2,&aGuid);
+
+ TInt newBuffSize;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetCollection, args));
+ if (newBuffSize > 0)
+ {
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetCollection, args));
+ }
+
+ CContactIdArray* idArray = iPackager->UnpackCntIdArrayLC();
+ CleanupStack::Pop(idArray);
+
+ return idArray;
+ }
+
+
+/**
+Set the sort preferences in the server.
+
+@param aSortOrder Sort order preferences.
+*/
+void RCntModel::SetSortPreferenceL(const CArrayFix<CContactDatabase::TSortPref>& aSortOrder)
+ {
+ TPtr8 ptr(iPackager->PackL(aSortOrder));
+ TIpcArgs args;
+ args.Set(0,&ptr);
+ User::LeaveIfError(SendReceive(ECntSetSortPrefs,args));
+ }
+
+
+/**
+Get the sort preferences from the server.
+
+@return Sort order preferences.
+*/
+CArrayFix<CContactDatabase::TSortPref>* RCntModel::GetSortPreferenceL() const
+ {
+ TIpcArgs args;
+ args.Set(0, &iPackager->GetReceivingBufferL());
+
+ TInt newBuffSize;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetSortPrefs, args));
+ if (newBuffSize > 0)
+ {
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntGetSortPrefs, args));
+ }
+
+ CArrayFix<CContactDatabase::TSortPref>* prefs = iPackager->UnpackCArrayFixLC();
+ CleanupStack::Pop(prefs);
+
+ return prefs;
+ }
+
+
+/**
+Get the number of contacts in the database.
+
+@return Number of contacts in the database if successful, otherwise it leaves.
+*/
+TInt RCntModel::ContactCountL() const
+ {
+ TInt numContacts = SendReceive( ECntDbContactCount );
+ User::LeaveIfError( numContacts );
+ return numContacts;
+ }
+
+/**
+Seek to the given contact ID.
+
+@param aContactId Contact ID to seek to.
+@param aId On return the contact ID that the seek found. This will be the
+nearest contact ID if the given aContactID cannot be found.
+@param aContactType On return the contact type of the contact that the seek
+found.
+@param aDeleted On return ETrue if the contact that the seek found is marked as
+deleted, EFalse otherwise.
+
+@return ETrue if the seek was successful, EFalse otherwise.
+*/
+TBool RCntModel::SeekContactL(TContactItemId aContactId,TContactItemId& aId,TUid& aContactType, TBool& aDeleted)
+ {
+ TPckgBuf<TInt> id;
+ TPckgBuf<TUid> type;
+ TPckgBuf<TBool> deleted;
+ TIpcArgs args(aContactId,&id,&type,&deleted);
+ TInt ret = SendReceive(ECntSeekContactInCollection,args);
+
+ // Although ECntSeekContact/ECntSeekContactInCollection is normally completed
+ // with TBool values, ServiceError() can occur which leads to a negative system
+ // error codes being returned. User::LeaveIfError will allow that to be properly
+ // handled.
+ User::LeaveIfError(ret);
+
+ if(ret)
+ {
+ aContactType = type();
+ aId = id();
+ aDeleted = deleted();
+ return ETrue;
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+
+
+void RCntModel::TextFieldL(TInt aCntItemId,TFieldType aFieldType, TDes& aText)
+ {
+ TPckgBuf<TFieldType> fieldType(aFieldType);
+ TIpcArgs args(aCntItemId,&fieldType,&aText);
+ User::LeaveIfError(SendReceive(ECntTextField,args));
+ }
+
+
+void RCntModel::ReadContactTextDefL(TContactItemId aContactId, TDes &aResult,const CContactTextDef& aTextDef)
+ {
+ iPackager->PackL( const_cast<CContactTextDef&>(aTextDef));
+ TIpcArgs args(&iPackager->GetTransmittingBuffer(), &aResult,aContactId);
+ User::LeaveIfError(SendReceive(ECntReadContactTextDef, args));
+ }
+
+
+CContactIdArray* RCntModel::FindL(const TDesC& aText,const CContactItemFieldDef* aFieldDef)
+ {
+ TIpcArgs args;
+
+ // Parameter can be NULL so send down an empty descriptor to the server.
+ // Server can detect this and pass NULL parameter to the Persistence Layer
+ // FindL() method.
+ if(aFieldDef != NULL)
+ {
+ // Packager's sending buffer in slot 1 (contains packed field
+ // definition).
+ iPackager->PackL(*aFieldDef);
+ args.Set(1,&iPackager->GetTransmittingBuffer());
+ }
+ else
+ {
+ // Frigged NULL.
+ args.Set(1,&KNullDesC);
+ }
+
+ // Packager's receiving buffer in slot 0.
+ args.Set(0, &iPackager->GetReceivingBufferL());
+
+ // Text in slot 2.
+ args.Set(2,&aText);
+
+ // Ensure receiving buffer is big enough.
+ TInt newBuffSize;
+ User::LeaveIfError(newBuffSize = SendReceive(ECntFind, args));
+ if (newBuffSize > 0)
+ {
+ // Not big enough so resize and call again.
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntFind, args));
+ }
+
+ CContactIdArray* idArray = iPackager->UnpackCntIdArrayLC();
+ CleanupStack::Pop(idArray);
+
+ return idArray;
+ }
+
+
+void RCntModel::SetDbViewContactType(const TUid aUid)
+ {
+ TIpcArgs args(aUid.iUid);
+ (void)SendReceive(ECntSetDbViewContactType,args);
+ }
+
+
+TUid RCntModel::GetDbViewContactType() const
+ {
+ TPckgBuf<TUid> type;
+ TIpcArgs args(&type);
+ (void)SendReceive(ECntSetDbViewContactType,args);
+ return type();
+ }
+
+
+/**
+Initialise the Persistence Layer collection class ready for iterative calls to
+the FindAsyncL() method. This form of initialisation is for an asynchronous
+find which uses a text definition and an array of "find words".
+
+@param aWords "Find words" array.
+@param aTextDef Text definition to use in find.
+*/
+void RCntModel::FindAsyncTextDefInitL(const CDesCArray& aWords,CContactTextDef* aTextDef)
+ {
+ TIpcArgs args;
+ if(aTextDef != NULL)
+ {
+ // Pack the text definition.
+ iPackager->PackL(*aTextDef);
+ args.Set(1,&iPackager->GetTransmittingBuffer());
+ }
+ else
+ {
+ // No user supplied text definition.
+ args.Set(1, &KNullDesC);
+ }
+
+ // Do the CDesCArray manually as Packager can't do both.
+ RBufWriteStream writeStream;
+ CBufFlat* buf = CBufFlat::NewL(1 << 8);
+ CleanupStack::PushL(buf);
+
+ writeStream.Open(*buf);
+ TInt count = aWords.Count();
+ writeStream.WriteInt32L(count);
+ TInt length = 0;
+ for(TInt i=0; i<count;++i)
+ {
+ length = aWords[i].Length();
+ writeStream.WriteInt32L(length);
+ writeStream.WriteL(aWords[i],length);
+ }
+ writeStream.CommitL();
+ writeStream.Close();
+
+ TPtr8 ptr8(buf->Ptr(0));
+ args.Set(0,&ptr8);
+
+ User::LeaveIfError(SendReceive(ECntFindAsyncTextDefInit,args));
+
+ CleanupStack::PopAndDestroy(buf);
+ }
+
+
+/**
+Initialise the Persistence Layer collection class ready for iterative calls to
+the FindAsyncL() method. This form of initialisation is for an asynchronous
+find which uses text and a field definition.
+
+@param aText Find text.
+@param aFieldDef Field definition to use in find.
+*/
+void RCntModel::FindAsyncInitL(const TDesC& aText,CContactItemFieldDef* aFieldDef)
+ {
+ TIpcArgs args;
+ args.Set(0,&aText);
+ if(aFieldDef != NULL)
+ {
+ // Pack the field definition.
+ iPackager->PackL(*aFieldDef);
+ args.Set(1,&iPackager->GetTransmittingBuffer());
+ }
+ else
+ {
+ // No user supplied field definition.
+ args.Set(1, &KNullDesC);
+ }
+
+ User::LeaveIfError(SendReceive(ECntFindAsyncInit,args));
+ }
+
+
+/**
+Perform an asynchronous find iteration. Either the FindAsyncTextDefInitL() or
+the FindAsyncInitL() method above must have first been called.
+
+@param aOrigIdArray Set of contacts IDs from previous calls to FindAsyncL().
+Will be NULL if this is the first iteration.
+
+@return ETrue if further find iterations are required, EFalse otherwise.
+*/
+TBool RCntModel::FindAsyncL(CContactIdArray*& aOrigIdArray)
+ {
+ TBool moreToGo;
+ TPckg<TBool> pckg(moreToGo);
+ // Iterations can't fail so expand buffer to 4Kb. 4096 contacts should be
+ // enough. Rather than this brutal approach we could call ContactCountL()
+ // and add a contingency in case another client adds some.
+ TIpcArgs args(&iPackager->GetReceivingBufferL(4096),&pckg);
+
+ User::LeaveIfError(SendReceive(ECntFindAsync,args));
+
+ CContactIdArray* newIdArray = iPackager->UnpackCntIdArrayLC();
+ if(aOrigIdArray != NULL)
+ {
+ // Append to the old array if it was not NULL.
+ for(TInt i=0;i<newIdArray->Count();++i)
+ {
+ aOrigIdArray->AddL((*newIdArray)[i]);
+ }
+ CleanupStack::PopAndDestroy(newIdArray);
+ }
+ else
+ {
+ CleanupStack::Pop(newIdArray);
+ aOrigIdArray = newIdArray;
+ }
+
+ return pckg();
+ }
+
+
+/**
+Set the asynchronous activity flag in the server. Any asynchronous activity
+such as asynchronous find or asynchronous sort prevent the database from
+being closed and should set this flag. When all asynchronous activites are
+finished this flag should be cleared.
+
+@capability None
+
+@param aAsyncActivity ETrue if at least one asynchronous activity, EFalse if
+no asynchronous activities.
+*/
+void RCntModel::SetAsyncActivityL(TBool aAsyncActivity)
+ {
+ TIpcArgs args;
+ args.Set(0,aAsyncActivity);
+ User::LeaveIfError(SendReceive(ECntSetAsyncActivity,args));
+ }
+
+
+/**
+Filter the database using the given filter.
+
+@param aFilter Filter to use. On return the contact IDs in this filter will be
+augmented with those contact IDs which match the given filter.
+*/
+void RCntModel::FilterDatabaseL(CCntFilter& aFilter)
+ {
+ CContactIdArray* origIdArray = aFilter.iIds;
+ aFilter.iIds = NULL;
+
+ TIpcArgs args;
+ iPackager->PackL(aFilter);
+ args.Set(1,&iPackager->GetTransmittingBuffer());
+ args.Set(0, &iPackager->GetReceivingBufferL());
+
+ TInt newBuffSize(0);
+ User::LeaveIfError(newBuffSize = SendReceive(ECntFilterDatabase, args));
+ if (newBuffSize > 0)
+ {
+ // Not big enough so resize and call again.
+ args.Set(0, &iPackager->GetReceivingBufferL(newBuffSize));
+ User::LeaveIfError(newBuffSize = SendReceive(ECntFilterDatabase, args));
+ }
+
+ CContactIdArray* newIdArray = iPackager->UnpackCntIdArrayLC();
+ if(origIdArray != NULL)
+ {
+ for(TInt i=0;i<newIdArray->Count();++i)
+ {
+ origIdArray->AddL((*newIdArray)[i]);
+ }
+ aFilter.iIds = origIdArray;
+ CleanupStack::PopAndDestroy(newIdArray);
+ }
+ else
+ {
+ CleanupStack::Pop(newIdArray);
+ aFilter.iIds = newIdArray;
+ }
+ }
+
+
+TInt RCntModel::OpenViewL(const CContactTextDef& aTextDef, const TInt aViewPrefs)
+ {
+ TPckgBuf<TInt> viewSessionId;
+ iPackager->PackL(const_cast<CContactTextDef&>(aTextDef));
+ TIpcArgs args(&iPackager->GetTransmittingBuffer(), aViewPrefs, &viewSessionId);
+
+ TInt ret = SendReceive(ECntOpenViewSession,args);
+ User::LeaveIfError(ret);
+
+ return viewSessionId();
+ }
+
+
+void RCntModel::CloseView(TInt aViewId)
+ {
+ TIpcArgs args(aViewId);
+ SendReceive(ECntCloseViewSession,args);
+ }
+
+
+void RCntModel::ChangeSortOrderL(TInt aViewId, const CContactTextDef& aTextDef)
+ {
+ // Serialize the text def using packager overload.
+ iPackager->PackL( const_cast<CContactTextDef&>(aTextDef));
+
+
+ TIpcArgs args(aViewId, &iPackager->GetTransmittingBuffer());
+ TInt ret = SendReceive(ECntViewChangeSortOrderL, args);
+
+ User::LeaveIfError(ret);
+ }
+
+
+void RCntModel::BeginIterateL(TInt aViewId)
+ {
+ TIpcArgs args(aViewId);
+ TInt ret = SendReceive(ECntViewBeginIterate,args);
+ User::LeaveIfError(ret);
+ }
+
+
+void RCntModel::EndIterateL(TInt aViewId)
+ {
+ TIpcArgs args(aViewId);
+ TInt ret = SendReceive(ECntViewEndIterate,args);
+ User::LeaveIfError(ret);
+ }
+
+
+CViewContact* RCntModel::NextItemL(TInt aViewId, const TInt aViewPrefs)
+ {
+ TIpcArgs args(aViewId, aViewPrefs, &iPackager->GetReceivingBufferL());
+ TInt size = SendReceive(ECntViewNextItemL, args);
+
+ // KErrNotFound is returned if Persistence Layer view item manager
+ // can't reach end of the contacts.
+ if(size == KErrNotFound)
+ {
+ // Not an error just no match.
+ return NULL;
+ }
+
+ // Leave on genuine errors.
+ User::LeaveIfError(size);
+
+ if(size != 0) // Server session requires larger packager buffer.
+ {
+ // Increase the buffer size and call again. Just in case some other
+ // client has deleted in between calls do the KErrNotFound check.
+ TIpcArgs args(aViewId, aViewPrefs, &iPackager->GetReceivingBufferL(size));
+ TInt ret = SendReceive(ECntViewNextItemL, args);
+ if(ret == KErrNotFound)
+ {
+ return NULL;
+ }
+ // Leave on genuine errors
+ User::LeaveIfError(ret);
+ }
+
+ // Got it so unpack and return.
+ CViewContact* viewContact = iPackager->UnpackViewContactLC();
+
+ CleanupStack::Pop(viewContact);
+
+ return viewContact;
+ }
+
+
+CViewContact* RCntModel::ItemAtL(TContactItemId aContactId, TInt aViewId)
+ {
+ TIpcArgs args(aContactId, aViewId, &iPackager->GetReceivingBufferL());
+ TInt size = SendReceive(ECntItemAtL, args);
+
+ // KErrNotFound is returned if Persistence Layer view item manager cannot
+ // find the view contact by given aContactId.
+ if(size == KErrNotFound)
+ {
+ // Not an error just no match.
+ return NULL;
+ }
+
+ // Leave on genuine errors.
+ User::LeaveIfError(size);
+
+ if(size != 0) // Server session requires larger packager buffer.
+ {
+ // Increase the buffer size and call again. Just in case some other
+ // client has deleted in between calls do the KErrNotFound check.
+ TIpcArgs args(aContactId, aViewId, &iPackager->GetReceivingBufferL(size));
+ TInt ret = SendReceive(ECntItemAtL, args);
+ if(ret == KErrNotFound)
+ {
+ return NULL;
+ }
+ // Leave on genuine errors
+ User::LeaveIfError(ret);
+ }
+
+ // Got it so unpack and return.
+ CViewContact* viewContact = iPackager->UnpackViewContactLC();
+
+ CleanupStack::Pop(viewContact);
+
+ return viewContact;
+ }
+
+
+#if defined(_DEBUG)
+void RCntModel::GetDefinitionsOfExistingViewsL(const TDesC& aDbName, RPointerArray<CContactDefaultViewDefinition>& aViewDefs)
+ {
+ RBuf8 buf;
+ CleanupClosePushL(buf);
+ buf.Create(256);
+ TIpcArgs args(&aDbName, &buf);
+
+ TInt newBufSize;
+ User::LeaveIfError(newBufSize = SendReceive(ECntGetDefinitionsForExistingView, args));
+ if (newBufSize > 0)
+ {
+ buf.ReAllocL(newBufSize);
+ args.Set(1, &buf);
+ User::LeaveIfError(newBufSize = SendReceive(ECntGetDefinitionsForExistingView, args));
+ }
+
+ RDesReadStream readStream(buf);
+ CleanupClosePushL(readStream);
+ TInt32 count;
+ readStream >> count;
+ CContactDefaultViewDefinition* view;
+ for (TInt i = 0; i < count; i++)
+ {
+ view = CContactDefaultViewDefinition::NewLC(readStream);
+ aViewDefs.AppendL(view);
+ CleanupStack::Pop(view);
+ }
+
+ CleanupStack::PopAndDestroy(2); // readStream, buf
+ }
+#else
+void RCntModel::GetDefinitionsOfExistingViewsL(const TDesC& /*aDbName*/, RPointerArray<CContactDefaultViewDefinition>& /*aViewDefs*/)
+ {
+ }
+#endif // _DEBUG