diff options
author | Jani Honkonen <jani.honkonen@digia.com> | 2010-04-29 16:52:02 +0300 |
---|---|---|
committer | Jani Honkonen <jani.honkonen@digia.com> | 2010-04-29 16:52:02 +0300 |
commit | e06260ffeefb253140ef175add64029da0e2c504 (patch) | |
tree | a5416f5d2dd74889fd4d5589b3b0acd3b44a7d3f | |
parent | a6daf5d256368a9673433b43b39d0c87fefb1bf9 (diff) |
Symbian SIM: Workaround for reading extra detail support.
We etel store api does not report nickname/additional number/email support correctly
and we cannot use RMmCustomAPI either because it's not a public API. Se we are left
with this trial and error method :(
10 files changed, 518 insertions, 157 deletions
diff --git a/plugins/contacts/symbiansim/inc/cntsimstore.h b/plugins/contacts/symbiansim/inc/cntsimstore.h index 9c3a152e7a..44ba51e457 100644 --- a/plugins/contacts/symbiansim/inc/cntsimstore.h +++ b/plugins/contacts/symbiansim/inc/cntsimstore.h @@ -53,13 +53,17 @@ QTM_USE_NAMESPACE -#ifdef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1 -typedef RMobilePhoneBookStore::TMobilePhoneBookInfoV1 TSimStoreInfo; -typedef RMobilePhoneBookStore::TMobilePhoneBookInfoV1Pckg TSimStoreInfoPckg; -#else -typedef RMobilePhoneBookStore::TMobilePhoneBookInfoV5 TSimStoreInfo; -typedef RMobilePhoneBookStore::TMobilePhoneBookInfoV5Pckg TSimStoreInfoPckg; -#endif +struct SimStoreInfo{ + QString m_storeName; + int m_totalEntries; + int m_usedEntries; + bool m_readOnlyAccess; + bool m_numberSupported; + bool m_nameSupported; + bool m_secondNameSupported; + bool m_additionalNumberSupported; + bool m_emailSupported; +}; class CntSimStorePrivate; class CntSymbianSimEngine; @@ -71,17 +75,13 @@ public: CntSimStore(CntSymbianSimEngine* engine, QString storeName, QContactManager::Error* error); ~CntSimStore(); - QString storeName(); - TSimStoreInfo storeInfo(); - + SimStoreInfo storeInfo(); bool read(int index, int numSlots, QContactManager::Error* error); bool write(const QContact &contact, QContactManager::Error* error); bool remove(int index, QContactManager::Error* error); bool getReservedSlots(QContactManager::Error* error); - void cancel(); bool isBusy(); - TInt lastAsyncError(); signals: diff --git a/plugins/contacts/symbiansim/inc/cntsimstoreprivate.h b/plugins/contacts/symbiansim/inc/cntsimstoreprivate.h index a4fc1b8502..27c316b585 100644 --- a/plugins/contacts/symbiansim/inc/cntsimstoreprivate.h +++ b/plugins/contacts/symbiansim/inc/cntsimstoreprivate.h @@ -73,14 +73,11 @@ public: static CntSimStorePrivate* NewL(CntSymbianSimEngine &engine, CntSimStore &simStore, const QString &storeName); ~CntSimStorePrivate(); - QString storeName() { return m_storeName; } - TSimStoreInfo storeInfo() { return m_storeInfo; } - + SimStoreInfo storeInfo() { return m_storeInfo; } bool read(int index, int numSlots, QContactManager::Error *error); bool write(const QContact &contact, QContactManager::Error *error); bool remove(int index, QContactManager::Error *error); bool getReservedSlots(QContactManager::Error *error); - TInt lastAsyncError() { return m_asyncError; } private: @@ -97,6 +94,9 @@ private: void encodeSimContactL(QContact* contact, TDes8& rawData) const; void putTagAndValueL(CPhoneBookBuffer* pbBuffer, TUint8 tag, QString data) const; QList<int> decodeReservedSlotsL(TDes8& rawData) const; + void writeL(QContact *contact); + void removeL(int index); + void updateStoreInfoL(); private: State m_state; @@ -106,15 +106,13 @@ private: RTelServer m_etelServer; RMobilePhone m_etelPhone; RMobilePhoneBookStore m_etelStore; - QString m_storeName; - bool m_readOnlyAccess; - TSimStoreInfo m_storeInfo; - TSimStoreInfoPckg m_storeInfoPckg; + SimStoreInfo m_storeInfo; RBuf8 m_buffer; QContact m_convertedContact; int m_writeIndex; CntSimStoreEventListener* m_listener; TInt m_asyncError; + bool m_extraDetailsChecked; }; #endif // CNTSIMSTOREPRIVATE_H_ diff --git a/plugins/contacts/symbiansim/src/cntsimcontactfetchrequest.cpp b/plugins/contacts/symbiansim/src/cntsimcontactfetchrequest.cpp index 23699b2a60..005c697c8b 100644 --- a/plugins/contacts/symbiansim/src/cntsimcontactfetchrequest.cpp +++ b/plugins/contacts/symbiansim/src/cntsimcontactfetchrequest.cpp @@ -74,7 +74,7 @@ void CntSimContactFetchRequest::run() // Contacts are fetched starting from index 1, all slots are read // since slots may be not filled in a sequence. int index = 1; - int numSlots = simStore()->storeInfo().iTotalEntries; + int numSlots = simStore()->storeInfo().m_totalEntries; if (lidFilter.ids().count() == 1) { // Optimization for performance. Fetch a single contact from store. diff --git a/plugins/contacts/symbiansim/src/cntsimcontactlocalidfetchrequest.cpp b/plugins/contacts/symbiansim/src/cntsimcontactlocalidfetchrequest.cpp index c9b6a8bb30..7de87d2c93 100644 --- a/plugins/contacts/symbiansim/src/cntsimcontactlocalidfetchrequest.cpp +++ b/plugins/contacts/symbiansim/src/cntsimcontactlocalidfetchrequest.cpp @@ -66,7 +66,7 @@ void CntSimContactLocalIdFetchRequest::run() // Contacts are fetched starting from index 1, all slots are read // since slots may be not filled in a sequence. int index = 1; - int numSlots = simStore()->storeInfo().iTotalEntries; + int numSlots = simStore()->storeInfo().m_totalEntries; QContactManager::Error error = QContactManager::NoError; if (!simStore()->read(index, numSlots, &error)) { diff --git a/plugins/contacts/symbiansim/src/cntsimstore.cpp b/plugins/contacts/symbiansim/src/cntsimstore.cpp index 8a0793f6d4..627382842a 100644 --- a/plugins/contacts/symbiansim/src/cntsimstore.cpp +++ b/plugins/contacts/symbiansim/src/cntsimstore.cpp @@ -65,12 +65,7 @@ CntSimStore::~CntSimStore() delete d_ptr; } -QString CntSimStore::storeName() -{ - return d_ptr->storeName(); -} - -TSimStoreInfo CntSimStore::storeInfo() +SimStoreInfo CntSimStore::storeInfo() { return d_ptr->storeInfo(); } diff --git a/plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp b/plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp index 3887c672ee..b72dbaa78b 100644 --- a/plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp +++ b/plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp @@ -67,13 +67,26 @@ CntSimStorePrivate::CntSimStorePrivate(CntSymbianSimEngine &engine, CntSimStore m_state(InactiveState), m_engine(engine), m_simStore(simStore), - m_storeName(storeName), - m_readOnlyAccess(false), - m_storeInfoPckg(m_storeInfo), - m_listener(0) + m_listener(0), + m_extraDetailsChecked(false) { CActiveScheduler::Add(this); m_managerUri = engine.managerUri(); + + // Initialize store info + m_storeInfo.m_storeName = storeName; + m_storeInfo.m_totalEntries = -1; + m_storeInfo.m_usedEntries = -1; + m_storeInfo.m_readOnlyAccess = false; + m_storeInfo.m_numberSupported = true; // allways supported + m_storeInfo.m_nameSupported = true; // allways supported + m_storeInfo.m_secondNameSupported = false; + m_storeInfo.m_additionalNumberSupported = false; + m_storeInfo.m_emailSupported = false; + + // SDN store is allways read only + if (m_storeInfo.m_storeName == KParameterValueSimStoreNameSdn) + m_storeInfo.m_readOnlyAccess = true; } void CntSimStorePrivate::ConstructL() @@ -81,10 +94,6 @@ void CntSimStorePrivate::ConstructL() TBuf<RMobilePhoneBookStore::KMaxPBIDSize> storeName; convertStoreNameL(storeName); - // SDN store is always read only - if (m_storeName == KParameterValueSimStoreNameSdn) - m_readOnlyAccess = true; - // Open etel server User::LeaveIfError(m_etelServer.Connect()); User::LeaveIfError(m_etelServer.LoadPhoneModule(KMmTsyModuleName)); @@ -94,26 +103,13 @@ void CntSimStorePrivate::ConstructL() User::LeaveIfError(m_etelServer.GetPhoneInfo(0, info)); User::LeaveIfError(m_etelPhone.Open(m_etelServer, info.iName)); - // open Etel store + // Open Etel store User::LeaveIfError(m_etelStore.Open(m_etelPhone, storeName)); + + // Update store info + updateStoreInfoL(); - //check what information can be saved to the Etel store - TRequestStatus requestStatus; - m_etelStore.GetInfo(requestStatus, (TDes8&)m_storeInfoPckg); - User::WaitForRequest(requestStatus); - PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - GetInfo err = %d"), - requestStatus.Int()); - User::LeaveIfError(requestStatus.Int()); - - PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - TotalEntries = %d"), - m_storeInfo.iTotalEntries); - PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - UsedEntries = %d"), - m_storeInfo.iUsedEntries); - PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - MaxNumLength = %d"), - m_storeInfo.iMaxNumLength); - PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - MaxTextLength = %d"), - m_storeInfo.iMaxTextLength); - + // Start listening for events m_listener = new (ELeave) CntSimStoreEventListener(m_engine, m_etelStore); m_listener->start(); } @@ -133,15 +129,15 @@ void CntSimStorePrivate::convertStoreNameL(TDes &storeName) User::Leave(KErrArgument); } - if (m_storeName.isEmpty()) { + if (m_storeInfo.m_storeName.isEmpty()) { // Default to ADN store - m_storeName = (QLatin1String) KParameterValueSimStoreNameAdn; + m_storeInfo.m_storeName = (QLatin1String) KParameterValueSimStoreNameAdn; storeName.Copy(KETelIccAdnPhoneBook); - } else if (m_storeName == KParameterValueSimStoreNameFdn) { + } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameFdn) { storeName.Copy(KETelIccFdnPhoneBook); - } else if (m_storeName == KParameterValueSimStoreNameAdn) { + } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameAdn) { storeName.Copy(KETelIccAdnPhoneBook); - } else if (m_storeName == KParameterValueSimStoreNameSdn) { + } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameSdn) { storeName.Copy(KETelIccSdnPhoneBook); } @@ -233,8 +229,8 @@ bool CntSimStorePrivate::getReservedSlots(QContactManager::Error *error) // start reading m_buffer.Zero(); - m_buffer.ReAlloc(KOneSimContactBufferSize*m_storeInfo.iTotalEntries); - m_etelStore.Read(iStatus, 1, m_storeInfo.iTotalEntries, m_buffer); + m_buffer.ReAlloc(KOneSimContactBufferSize*m_storeInfo.m_totalEntries); + m_etelStore.Read(iStatus, 1, m_storeInfo.m_totalEntries, m_buffer); SetActive(); m_state = ReadReservedSlotsState; @@ -417,7 +413,7 @@ QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const QString number = lastNumber.number(); number.insert(0, "+"); lastNumber.setNumber(number); - if (m_readOnlyAccess) + if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&lastNumber); currentContact.saveDetail(&lastNumber); } @@ -441,7 +437,7 @@ QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const QContactName name; QString nameString = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); name.setCustomLabel(nameString); - if (m_readOnlyAccess) + if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&name); currentContact.saveDetail(&name); QContactManager::Error error(QContactManager::NoError); @@ -456,7 +452,7 @@ QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const QContactNickname nickName; QString name = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); nickName.setNickname(name); - if (m_readOnlyAccess) + if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&nickName); currentContact.saveDetail(&nickName); } @@ -468,7 +464,7 @@ QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const QContactPhoneNumber phoneNumber; QString number = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); phoneNumber.setNumber(number); - if (m_readOnlyAccess) + if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&phoneNumber); currentContact.saveDetail(&phoneNumber); } @@ -486,7 +482,7 @@ QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const QContactEmailAddress email; QString emailAddress = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); email.setEmailAddress(emailAddress); - if (m_readOnlyAccess) + if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&email); currentContact.saveDetail(&email); } @@ -531,46 +527,45 @@ QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const */ void CntSimStorePrivate::encodeSimContactL(QContact* contact, TDes8& rawData) const { - // TODO: get detail definition schema and verify (unique) - // Keep track of the count of phone numbers added int phoneNumberCount(0); - int emailCount(0); + // Create buffer CPhoneBookBuffer* pbBuffer = new(ELeave) CPhoneBookBuffer(); CleanupStack::PushL(pbBuffer); pbBuffer->Set(&rawData); User::LeaveIfError(pbBuffer->AddNewEntryTag()); - foreach(QContactDetail detail, contact->details()) { + // Loop through details + foreach(QContactDetail detail, contact->details()) + { QString definitionName = detail.definitionName(); + + // NOTE: If the detail is too long let the etel store return + // an error about it. We could check the maximum lenghts for each + // detail but then we would need to read it before every write operation + // bacause it seems to change depending on how full the sim card is. - if (definitionName == QContactName::DefinitionName) { - // Name + // Name + if (definitionName == QContactName::DefinitionName) + { QContactName nameDetail = static_cast<QContactName>(detail); - // Trim to the max possible length - QString name = nameDetail.customLabel().left(m_storeInfo.iMaxTextLength); - if (name.isEmpty()) { + QString name = nameDetail.customLabel(); + if (name.isEmpty()) name = "Unnamed"; - } - putTagAndValueL( - pbBuffer, - RMobilePhoneBookStore::ETagPBText, - name); - // Replace detail value with the trimmed one - nameDetail.setCustomLabel(name); - contact->saveDetail(&nameDetail); - } else if (definitionName == QContactPhoneNumber::DefinitionName -#ifndef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1 - && (phoneNumberCount == 0 - || phoneNumberCount <= m_storeInfo.iMaxAdditionalNumbers)) { -#else - && phoneNumberCount == 0) { -#endif - // Phone number + + putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBText, name); + } + // Phone number + else if (definitionName == QContactPhoneNumber::DefinitionName) + { + if (m_storeInfo.m_additionalNumberSupported == false && phoneNumberCount>0) + User::Leave(KErrNotSupported); + phoneNumberCount++; - QContactPhoneNumber numberDetail = static_cast<QContactPhoneNumber>(detail); - QString number = numberDetail.number(); + QString number = static_cast<QContactPhoneNumber>(detail).number(); + if (number.isEmpty()) + continue; // Verify the number only contains legal digits foreach (const QChar character, number) { @@ -585,58 +580,48 @@ void CntSimStorePrivate::encodeSimContactL(QContact* contact, TDes8& rawData) co } } - // TODO: check if the number is empty (do we have a test case for that?) - - // Verify the number length - PbkPrintToLog(_L("CntSymbianSimEngine::encodeSimContactL() - phone number length = %d"), - numberDetail.number().length()); - if (numberDetail.number().length() > m_storeInfo.iMaxNumLength) { - User::Leave(KErrTooBig); - } - if (phoneNumberCount > 1) { // Mark the beginning of an additional number User::LeaveIfError(pbBuffer->AddNewNumberTag()); } // The number itself - putTagAndValueL( - pbBuffer, - RMobilePhoneBookStore::ETagPBNumber, - number); -#ifndef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1 - // Phonebook info version 1 does not support nick name or e-mail - } else if (definitionName == QContactNickname::DefinitionName - && m_storeInfo.iMaxSecondNames > 0) { - // nickname - QContactNickname nicknameDetail = static_cast<QContactNickname>(detail); - // Trim to the max possible length; - QString nickname = nicknameDetail.nickname().left(m_storeInfo.iMaxTextLengthSecondName); - putTagAndValueL( - pbBuffer, - RMobilePhoneBookStore::ETagPBSecondName, - nickname); - // Replace detail value with the trimmed one - nicknameDetail.setNickname(nickname); - contact->saveDetail(&nicknameDetail); - } else if (definitionName == QContactEmailAddress::DefinitionName - && emailCount < m_storeInfo.iMaxEmailAddr) { - emailCount++; - QContactEmailAddress emailDetail = static_cast<QContactEmailAddress>(detail); - if (emailDetail.emailAddress().length() > m_storeInfo.iMaxTextLengthEmailAddr) { - User::Leave(KErrTooBig); - } - putTagAndValueL( - pbBuffer, - RMobilePhoneBookStore::ETagPBEmailAddress, - emailDetail.emailAddress()); -#endif + putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBNumber, number); + } + // Nickname + else if (definitionName == QContactNickname::DefinitionName) + { + if (m_storeInfo.m_secondNameSupported == false) + User::Leave(KErrNotSupported); + + QString nickname = static_cast<QContactNickname>(detail).nickname(); + if (nickname.isEmpty()) + continue; + + putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBSecondName, nickname); + } + // email + else if (definitionName == QContactEmailAddress::DefinitionName) + { + if (m_storeInfo.m_emailSupported == false) + User::Leave(KErrNotSupported); + + QString email = static_cast<QContactEmailAddress>(detail).emailAddress(); + if (email.isEmpty()) + continue; + + putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBEmailAddress, email); + } // These are ignored in the conversion - } else if (definitionName == QContactSyncTarget::DefinitionName + else if (definitionName == QContactSyncTarget::DefinitionName || definitionName == QContactDisplayLabel::DefinitionName - || definitionName == QContactType::DefinitionName) { + || definitionName == QContactType::DefinitionName) + { // Do nothing - } else { + } + else + { + // Unknown detail User::Leave(KErrArgument); } } @@ -675,3 +660,154 @@ QList<int> CntSimStorePrivate::decodeReservedSlotsL(TDes8& rawData) const CleanupStack::PopAndDestroy(pbBuffer); return reservedSlots; } + +void CntSimStorePrivate::writeL(QContact *contact) +{ + if (IsActive()) + User::Leave(KErrLocked); + + // get index + int index = KErrNotFound; + if (contact->id().managerUri() == m_managerUri && + contact->localId() > 0) { + index = contact->localId(); + } + + // encode + m_buffer.Zero(); + m_buffer.ReAlloc(KOneSimContactBufferSize); + encodeSimContactL(contact, m_buffer); + + // write + TRequestStatus status; + m_etelStore.Write(status, m_buffer, index); + User::WaitForRequest(status); + User::LeaveIfError(status.Int()); + + // update id + QContactId id; + id.setLocalId(index); + id.setManagerUri(m_managerUri); + contact->setId(id); +} + +void CntSimStorePrivate::removeL(int index) +{ + if (IsActive()) + User::Leave(KErrLocked); + + // NOTE: + // If index points to an empty slot and running in hardware the + // delete operation will not return any error. + + TRequestStatus status; + m_etelStore.Delete(status, index); + User::WaitForRequest(status); + User::LeaveIfError(status.Int()); +} + +void CntSimStorePrivate::updateStoreInfoL() +{ +#ifdef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1 + RMobilePhoneBookStore::TMobilePhoneBookInfoV1 info; + RMobilePhoneBookStore::TMobilePhoneBookInfoV1Pckg infoPckg(info); +#else + RMobilePhoneBookStore::TMobilePhoneBookInfoV5 info; + RMobilePhoneBookStore::TMobilePhoneBookInfoV5Pckg infoPckg(info); +#endif + + // Get info + TRequestStatus status; + m_etelStore.GetInfo(status, infoPckg); + User::WaitForRequest(status); + User::LeaveIfError(status.Int()); + + // Update entry counts + m_storeInfo.m_totalEntries = info.iTotalEntries; + m_storeInfo.m_usedEntries = info.iUsedEntries; + +#if SYMBIANSIM_BACKEND_TEST_EXTRADETAILS + // Check if store supports the extra details + // + // NOTE: + // We cannot rely on TMobilePhoneBookInfoV5 to check if we support + // these details. For example iMaxSecondNames is allways -1 even if the sim + // card supports a second name. + // + // There is an API for checking these but it's Nokia internal so we must + // do it this way - by checking if saving these details is possible. + + // Have we checked these already? + if (m_extraDetailsChecked == false) + { + // Cannot test extra details if sim card is full + if (m_storeInfo.m_usedEntries == m_storeInfo.m_totalEntries) + return; + + // Cancel store event listener temporarily + if (m_listener) + m_listener->Cancel(); + + // Test writing nickname + QContact contact; + QContactNickname nick; + nick.setNickname("simbackend test"); + contact.saveDetail(&nick); + TRAPD(err, { + m_storeInfo.m_secondNameSupported = true; // enable to pass encodeSimContactL() + writeL(&contact); + removeL(contact.localId()); + } ); + if (err) + m_storeInfo.m_secondNameSupported = false; + + // Test writing additional number + contact = QContact(); + QContactPhoneNumber num1; + num1.setNumber("1111111111"); + contact.saveDetail(&num1); + QContactPhoneNumber num2; + num2.setNumber("2222222222"); + contact.saveDetail(&num2); + TRAP(err, { + m_storeInfo.m_additionalNumberSupported = true; // enable to pass encodeSimContactL() + writeL(&contact); + removeL(contact.localId()); + } ); + if (err) + m_storeInfo.m_additionalNumberSupported = false; + + // Test writing email + contact = QContact(); + QContactEmailAddress email; + email.setEmailAddress("simbackend@test.com"); + contact.saveDetail(&email); + TRAP(err, { + m_storeInfo.m_emailSupported = true; // enable to pass encodeSimContactL() + writeL(&contact); + removeL(contact.localId()); + } ); + if (err) + m_storeInfo.m_emailSupported = false; + + // Start store event listener again + if (m_listener) + m_listener->start(); + + m_extraDetailsChecked = true; + } +#endif + + /* + qDebug() << "Store info:" + << "\nStore name :" << m_storeInfo.m_storeName + << "\nTotal entries :" << m_storeInfo.m_totalEntries + << "\nUsed entries :" << m_storeInfo.m_usedEntries + << "\nRead only access :" << m_storeInfo.m_readOnlyAccess + << "\nNumber supported :" << m_storeInfo.m_numberSupported + << "\nName supported :" << m_storeInfo.m_nameSupported + << "\nSecond name supported :" << m_storeInfo.m_secondNameSupported + << "\nAdditional name supported :" << m_storeInfo.m_additionalNumberSupported + << "\nEmail supported :" << m_storeInfo.m_emailSupported; + */ +} diff --git a/plugins/contacts/symbiansim/src/cntsymbiansimengine.cpp b/plugins/contacts/symbiansim/src/cntsymbiansimengine.cpp index c34d9e4efb..249a2b674c 100644 --- a/plugins/contacts/symbiansim/src/cntsymbiansimengine.cpp +++ b/plugins/contacts/symbiansim/src/cntsymbiansimengine.cpp @@ -86,10 +86,10 @@ CntSymbianSimEngine::CntSymbianSimEngine(const QMap<QString, QString>& parameter return; } - if(d->m_simStore->storeName() == KParameterValueSimStoreNameSdn) { + if(d->m_simStore->storeInfo().m_storeName == KParameterValueSimStoreNameSdn) { // In case of SDN store we need to check if any SDN contacts exist to // determine if the store is supported or not - if(d->m_simStore->storeInfo().iUsedEntries == 0) + if(d->m_simStore->storeInfo().m_usedEntries == 0) *error = QContactManager::NotSupportedError; } } @@ -222,7 +222,7 @@ QMap<QString, QContactDetailDefinition> CntSymbianSimEngine::detailDefinitions(c } // Get store information - TSimStoreInfo storeInfo = d->m_simStore->storeInfo(); + SimStoreInfo storeInfo = d->m_simStore->storeInfo(); // the map we will eventually return QMap<QString, QContactDetailDefinition> retn; @@ -285,8 +285,7 @@ QMap<QString, QContactDetailDefinition> CntSymbianSimEngine::detailDefinitions(c retn.insert(def.name(), def); // email support needs to be checked run-time, because it is SIM specific -#ifndef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1 - if (storeInfo.iMaxEmailAddr > 0) { + if (storeInfo.m_emailSupported) { def.setName(QContactEmailAddress::DefinitionName); fields.clear(); f.setDataType(QVariant::String); @@ -296,7 +295,6 @@ QMap<QString, QContactDetailDefinition> CntSymbianSimEngine::detailDefinitions(c def.setUnique(true); retn.insert(def.name(), def); } -#endif // phone number def.setName(QContactPhoneNumber::DefinitionName); @@ -306,23 +304,17 @@ QMap<QString, QContactDetailDefinition> CntSymbianSimEngine::detailDefinitions(c fields.insert(QContactPhoneNumber::FieldNumber, f); // TODO: subtypes supported in case a sim contact can have multiple phone numbers? def.setFields(fields); -#ifndef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1 - if (storeInfo.iMaxAdditionalNumbers > 0) { + if (storeInfo.m_additionalNumberSupported) { // multiple numbers supported def.setUnique(false); } else { // only one phone number allowed def.setUnique(true); } -#else - // only one phone number allowed - def.setUnique(true); -#endif retn.insert(def.name(), def); // nickname support needs to be checked run-time, because it is SIM specific -#ifndef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1 - if (storeInfo.iMaxSecondNames > 0) { + if (storeInfo.m_secondNameSupported) { def.setName(QContactNickname::DefinitionName); fields.clear(); f.setDataType(QVariant::String); @@ -332,7 +324,6 @@ QMap<QString, QContactDetailDefinition> CntSymbianSimEngine::detailDefinitions(c def.setUnique(true); retn.insert(def.name(), def); } -#endif // name def.setName(QContactName::DefinitionName); diff --git a/plugins/contacts/symbiansim/src/cntsymbiansimtransformerror.cpp b/plugins/contacts/symbiansim/src/cntsymbiansimtransformerror.cpp index fd14581e08..3e0265284d 100644 --- a/plugins/contacts/symbiansim/src/cntsymbiansimtransformerror.cpp +++ b/plugins/contacts/symbiansim/src/cntsymbiansimtransformerror.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "cntsymbiansimtransformerror.h" +#include <GsmError.h> /*! Transform a Symbian contact error id to QContactManager::Error. * @@ -91,6 +92,13 @@ void CntSymbianSimTransformError::transformError(TInt symbianError, QContactMana *qtError = QContactManager::BadArgumentError; break; } + case KErrGsmSimServSneFull: + case KErrGsmSimServAnrFull: + case KErrGsmSimServEmailFull: + { + *qtError = QContactManager::LimitReachedError; + break; + } default: { *qtError = QContactManager::UnspecifiedError; diff --git a/plugins/contacts/symbiansim/symbiansim_defines.pri b/plugins/contacts/symbiansim/symbiansim_defines.pri index 5e41cb0441..c5863caa60 100644 --- a/plugins/contacts/symbiansim/symbiansim_defines.pri +++ b/plugins/contacts/symbiansim/symbiansim_defines.pri @@ -33,11 +33,19 @@ symbian: { # remove operation has completed the device reboots. DEFINES += SYMBIANSIM_BACKEND_CHECK_BEFORE_REMOVE - # In pre 10.1 platforms we need a small delay between requests to prevent - # S60 3.2 devices from rebooting and S60 5.0 devices from reporting a - # server busy error. Not sure if this is really needed for S60 3.1 but - # it does not hurt. contains(S60_VERSION, 3.1) | contains(S60_VERSION, 3.2) | contains(S60_VERSION, 5.0) { + + # In pre 10.1 platforms we need a small delay between requests to prevent + # S60 3.2 devices from rebooting and S60 5.0 devices from reporting a + # server busy error. Not sure if this is really needed for S60 3.1 but + # it does not hurt. DEFINES += SYMBIANSIM_BACKEND_USE_DELAY + + # In pre 10.1 platforms we need to check extra detail support + # (nickname/additional number/email) by trying to write them to sim card. + # This is because when using RMobilePhoneStore::GetInfo() it does not + # report them correctly. There is another API for checking this but + # it cannot be used as it is not public on these platforms. + DEFINES += SYMBIANSIM_BACKEND_TEST_EXTRADETAILS } } diff --git a/plugins/contacts/symbiansim/tsrc/tst_simcm/tst_simcm.cpp b/plugins/contacts/symbiansim/tsrc/tst_simcm/tst_simcm.cpp index f9338a169e..94d68d9408 100644 --- a/plugins/contacts/symbiansim/tsrc/tst_simcm/tst_simcm.cpp +++ b/plugins/contacts/symbiansim/tsrc/tst_simcm/tst_simcm.cpp @@ -111,6 +111,7 @@ private slots: /* Test cases that take no data */ void signalEmission(); void sdnContacts(); + void fillSlots(); private: void initManager(QString simStore); @@ -120,6 +121,10 @@ private: void compareDetails(QContact contact, QList<QContactDetail> expectedDetails); QContact createContact(QString name, QString number); QContact saveContact(QString name, QString number); + void dumpStoreInfo(); + bool compareContactLists(QList<QContact> lista, QList<QContact> listb); + bool compareContacts(QContact ca, QContact cb); + private: QContactManager* m_cm; @@ -1002,8 +1007,8 @@ void tst_SimCM::detailFilter_data() << detail << field << "313" << (int) QContactFilter::MatchPhoneNumber << "h"; // Custom label - detail = (QString) QContactName::DefinitionName; - field = (QString) QContactName::FieldCustomLabel; + detail = (QLatin1String) QContactName::DefinitionName; + field = (QLatin1String) QContactName::FieldCustomLabel; QTest::newRow("customlabel=frederik") << detail << field << "frederik" << 0 << "c"; @@ -1165,6 +1170,147 @@ void tst_SimCM::sdnContacts() QVERIFY(!cm->saveContact(&c)); } +void tst_SimCM::fillSlots() +{ + initManager("ADN"); + + // remove all contacts + QList<QContactLocalId> ids = m_cm->contactIds(); + m_cm->removeContacts(ids, 0); + + // Update store info for empty sim card + TRAPD(err, getEtelStoreInfoL(KETelIccAdnPhoneBook, m_etelStoreInfoPckg)); + QVERIFY(err == KErrNone); + //dumpStoreInfo(); + + // Get detail definitions + QMap<QString, QContactDetailDefinition> defs = m_cm->detailDefinitions(); + bool nicknameSupported = defs.contains(QContactNickname::DefinitionName); + bool additionalNumberSupported = !defs.value(QContactPhoneNumber::DefinitionName).isUnique(); + bool emailSupported = defs.contains(QContactNickname::DefinitionName); + + // Fill all slots with a name + QList<QContact> savedContacts; + int i; + for (i=0; i<m_etelStoreInfo.iTotalEntries; i++) + { + QContact c; + QString label; + label.fill('x', 10); + QString tmp = QString("%1-").arg(i); + label.replace(0, tmp.size(), tmp); + QContactName name; + name.setCustomLabel(label); + c.saveDetail(&name); + QVERIFY(m_cm->saveContact(&c)); + savedContacts << c; + } + qDebug() << QString("Wrote %1 contacts with a name").arg(i); + + // Sim card should be full now. Try writing one more. + { + QContact c; + QContactName name; + name.setCustomLabel("foobar"); + c.saveDetail(&name); + QVERIFY(!m_cm->saveContact(&c)); + } + + // Write all slots with a number + for (i=0; i<m_etelStoreInfo.iTotalEntries; i++) + { + QContact &c = savedContacts[i]; + QString num; + num.fill('0', 10); + QString tmp = QString("%1#").arg(i); + num.replace(0, tmp.size(), tmp); + QContactPhoneNumber number; + number.setNumber(num); + c.saveDetail(&number); + QVERIFY(m_cm->saveContact(&c)); + } + qDebug() << QString("Wrote %1 contacts with a number").arg(i); + + // Write all slots with a nickname + for (i=0; i<m_etelStoreInfo.iTotalEntries && nicknameSupported; i++) + { + QContact c = savedContacts[i]; + QContactNickname nickname; + QString nick; + nick.fill('x', 10); + QString tmp = QString("%1-").arg(i); + nick.replace(0, tmp.size(), tmp); + nickname.setNickname(nick); + c.saveDetail(&nickname); + if (!m_cm->saveContact(&c)) { + if (m_cm->error() == QContactManager::LimitReachedError) + break; + else + QFAIL("Failed to write nickname"); + } + savedContacts[i] = c; + } + qDebug() << QString("Wrote %1 contacts with a nickname").arg(i); + + // Write all slots with a additional number + for (i=0; i<m_etelStoreInfo.iTotalEntries && additionalNumberSupported; i++) + { + QContact c = savedContacts[i]; + QString num; + num.fill('0', 10); + QString tmp = QString("%1#1").arg(i); + num.replace(0, tmp.size(), tmp); + QContactPhoneNumber additionalNumber; + additionalNumber.setNumber(num); + c.saveDetail(&additionalNumber); + if (!m_cm->saveContact(&c)) { + if (m_cm->error() == QContactManager::LimitReachedError) + break; + else + QFAIL("Failed to write additional number"); + } + savedContacts[i] = c; + } + qDebug() << QString("Wrote %1 contacts with additional number").arg(i); + + // Write all slots with a email + for (i=0; i<m_etelStoreInfo.iTotalEntries && emailSupported; i++) + { + QContact c = savedContacts[i]; + QContactEmailAddress emailAddress; + QString email; + email.fill('x', 10); + QString tmp = QString("%1@").arg(i); + email.replace(0, tmp.size(), tmp); + emailAddress.setEmailAddress(email); + c.saveDetail(&emailAddress); + if (!m_cm->saveContact(&c)) { + if (m_cm->error() == QContactManager::LimitReachedError) + break; + else + QFAIL("Failed to write email"); + } + savedContacts[i] = c; + } + qDebug() << QString("Wrote %1 contacts with an email").arg(i); + + TRAP(err, getEtelStoreInfoL(KETelIccAdnPhoneBook, m_etelStoreInfoPckg)); + QVERIFY(err == KErrNone); + //dumpStoreInfo(); + + QList<QContact> contacts = m_cm->contacts(); +#ifdef __WINS__ + // Cannot do full compare in emulator because of etel test server bug. + // If saving for example nickname fails with QContactManager::LimitReachedError + // it will still save the contact. + QVERIFY(contacts.count() == savedContacts.count()); +#else + QVERIFY(compareContactLists(contacts, savedContacts)); +#endif +} + + + /*! * Private helper function for checking the data format that the store supports */ @@ -1351,5 +1497,84 @@ QContact tst_SimCM::saveContact(QString name, QString number) return c; } +void tst_SimCM::dumpStoreInfo() +{ + TPtrC name = m_etelStoreInfo.iName; + TPtrC8 identity = m_etelStoreInfo.iIdentity; + + qDebug() << "Store info:" + << "\nType " << m_etelStoreInfo.iType + << "\nTotalEntries " << m_etelStoreInfo.iTotalEntries + << "\nUsedEntries " << m_etelStoreInfo.iUsedEntries + << "\nCaps " << m_etelStoreInfo.iCaps + << "\nName " << QString::fromUtf16(name.Ptr(), name.Length()) + << "\nMaxNumLength " << m_etelStoreInfo.iMaxNumLength + << "\nMaxTextLength " << m_etelStoreInfo.iMaxTextLength + << "\nLocation " << m_etelStoreInfo.iLocation + << "\nChangeCounter " << m_etelStoreInfo.iChangeCounter + << "\nIdentity " << QString::fromUtf8((const char*)identity.Ptr(), identity.Length()) +#ifdef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1 + ; +#else + << "\nMaxSecondNames " << m_etelStoreInfo.iMaxSecondNames + << "\nMaxTextLengthSecondName " << m_etelStoreInfo.iMaxTextLengthSecondName + << "\nMaxAdditionalNumbers " << m_etelStoreInfo.iMaxAdditionalNumbers + << "\nMaxNumLengthAdditionalNumber " << m_etelStoreInfo.iMaxNumLengthAdditionalNumber + << "\nMaxTextLengthAdditionalNumber" << m_etelStoreInfo.iMaxTextLengthAdditionalNumber + << "\nMaxGroupNames " << m_etelStoreInfo.iMaxGroupNames + << "\nMaxTextLengthGroupName " << m_etelStoreInfo.iMaxTextLengthGroupName + << "\nMaxEmailAddr " << m_etelStoreInfo.iMaxEmailAddr + << "\nMaxTextLengthEmailAddr " << m_etelStoreInfo.iMaxTextLengthEmailAddr; +#endif +} + +bool tst_SimCM::compareContactLists(QList<QContact> lista, QList<QContact> listb) +{ + // NOTE: This compare is contact order insensitive. + + // Remove matching contacts + foreach (QContact a, lista) { + foreach (QContact b, listb) { + if (compareContacts(a, b)) { + lista.removeOne(a); + listb.removeOne(b); + break; + } + } + } + + //if (lista.count() != 0) qDebug() << "\nList A:\n" << lista; + //if (listb.count() != 0) qDebug() << "\nList B:\n" << listb; + + return (lista.count() == 0 && listb.count() == 0); +} + +bool tst_SimCM::compareContacts(QContact ca, QContact cb) +{ + // NOTE: This compare is contact detail order insensitive. + + if (ca.localId() != cb.localId()) + return false; + + QList<QContactDetail> aDetails = ca.details(); + QList<QContactDetail> bDetails = cb.details(); + + // Remove matching details + foreach (QContactDetail ad, aDetails) { + foreach (QContactDetail bd, bDetails) { + if (ad == bd) { + ca.removeDetail(&ad); + cb.removeDetail(&bd); + break; + } + } + } + + if (ca != cb) + qDebug() << "\nCompare failed:\n" << "A:\n" << ca << "\nB:\n" << cb; + + return (ca == cb); +} + QTEST_MAIN(tst_SimCM) #include "tst_simcm.moc" |