summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJani Honkonen <jani.honkonen@digia.com>2010-04-29 16:52:02 +0300
committerJani Honkonen <jani.honkonen@digia.com>2010-04-29 16:52:02 +0300
commite06260ffeefb253140ef175add64029da0e2c504 (patch)
treea5416f5d2dd74889fd4d5589b3b0acd3b44a7d3f
parenta6daf5d256368a9673433b43b39d0c87fefb1bf9 (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 :(
-rw-r--r--plugins/contacts/symbiansim/inc/cntsimstore.h24
-rw-r--r--plugins/contacts/symbiansim/inc/cntsimstoreprivate.h14
-rw-r--r--plugins/contacts/symbiansim/src/cntsimcontactfetchrequest.cpp2
-rw-r--r--plugins/contacts/symbiansim/src/cntsimcontactlocalidfetchrequest.cpp2
-rw-r--r--plugins/contacts/symbiansim/src/cntsimstore.cpp7
-rw-r--r--plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp352
-rw-r--r--plugins/contacts/symbiansim/src/cntsymbiansimengine.cpp21
-rw-r--r--plugins/contacts/symbiansim/src/cntsymbiansimtransformerror.cpp8
-rw-r--r--plugins/contacts/symbiansim/symbiansim_defines.pri16
-rw-r--r--plugins/contacts/symbiansim/tsrc/tst_simcm/tst_simcm.cpp229
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"