plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "cntsimstoreprivate.h"
       
    43 #include "cntsymbiansimtransformerror.h"
       
    44 #include "cntsimstore.h"
       
    45 #include "cntsimstoreeventlistener.h"
       
    46 
       
    47 #include <mmtsy_names.h>
       
    48 #include <qtcontacts.h>
       
    49 #include <qcontactchangeset.h>
       
    50 #include <QDebug>
       
    51 
       
    52 const TInt KOneSimContactBufferSize = 512;
       
    53 const TInt KDataClientBuf  = 128;
       
    54 const TInt KEtsiTonPosition = 0x70;
       
    55 
       
    56 CntSimStorePrivate* CntSimStorePrivate::NewL(CntSymbianSimEngine &engine, CntSimStore &simStore, const QString &storeName)
       
    57 {
       
    58     CntSimStorePrivate* self = new (ELeave) CntSimStorePrivate(engine, simStore, storeName);
       
    59     CleanupStack::PushL(self);
       
    60     self->ConstructL();
       
    61     CleanupStack::Pop(self);
       
    62     return self;
       
    63 }
       
    64 
       
    65 CntSimStorePrivate::CntSimStorePrivate(CntSymbianSimEngine &engine, CntSimStore &simStore, const QString &storeName)
       
    66     :CActive(CActive::EPriorityStandard),
       
    67      m_state(InactiveState),
       
    68      m_engine(engine),
       
    69      m_simStore(simStore),
       
    70      m_listener(0),
       
    71      m_extraDetailsChecked(false)
       
    72 {
       
    73     CActiveScheduler::Add(this);
       
    74     m_managerUri = engine.managerUri();
       
    75     
       
    76     // Initialize store info
       
    77     m_storeInfo.m_storeName                 = storeName;
       
    78     m_storeInfo.m_totalEntries              = -1;
       
    79     m_storeInfo.m_usedEntries               = -1;
       
    80     m_storeInfo.m_readOnlyAccess            = false; 
       
    81     m_storeInfo.m_numberSupported           = true; // allways supported
       
    82     m_storeInfo.m_nameSupported             = true; // allways supported
       
    83     m_storeInfo.m_secondNameSupported       = false;
       
    84     m_storeInfo.m_additionalNumberSupported = false;
       
    85     m_storeInfo.m_emailSupported            = false;
       
    86     
       
    87     // SDN store is allways read only
       
    88     if (m_storeInfo.m_storeName == KParameterValueSimStoreNameSdn)
       
    89         m_storeInfo.m_readOnlyAccess = true;
       
    90 }
       
    91 
       
    92 void CntSimStorePrivate::ConstructL()
       
    93 {
       
    94     TBuf<RMobilePhoneBookStore::KMaxPBIDSize> storeName;
       
    95     convertStoreNameL(storeName);
       
    96     
       
    97     // Open etel server
       
    98     User::LeaveIfError(m_etelServer.Connect());
       
    99     User::LeaveIfError(m_etelServer.LoadPhoneModule(KMmTsyModuleName));
       
   100 
       
   101     // Open etel phone
       
   102     RTelServer::TPhoneInfo info;
       
   103     User::LeaveIfError(m_etelServer.GetPhoneInfo(0, info));
       
   104     User::LeaveIfError(m_etelPhone.Open(m_etelServer, info.iName));
       
   105     
       
   106     // Open Etel store
       
   107     User::LeaveIfError(m_etelStore.Open(m_etelPhone, storeName));
       
   108 
       
   109     // Update store info
       
   110     updateStoreInfoL();
       
   111     
       
   112     // Start listening for events
       
   113     m_listener = new (ELeave) CntSimStoreEventListener(m_engine, m_etelStore);
       
   114     m_listener->start();
       
   115 }
       
   116 
       
   117 CntSimStorePrivate::~CntSimStorePrivate()
       
   118 {
       
   119     Cancel();
       
   120     delete m_listener;
       
   121     m_etelStore.Close();
       
   122     m_etelPhone.Close();
       
   123     m_etelServer.Close();
       
   124 }
       
   125 
       
   126 void CntSimStorePrivate::convertStoreNameL(TDes &storeName)
       
   127 {
       
   128     if(storeName.MaxLength() < RMobilePhoneBookStore::KMaxPBIDSize) {
       
   129         User::Leave(KErrArgument);
       
   130     }
       
   131 
       
   132     if (m_storeInfo.m_storeName.isEmpty()) {
       
   133         // Default to ADN store
       
   134         m_storeInfo.m_storeName = (QLatin1String) KParameterValueSimStoreNameAdn;
       
   135         storeName.Copy(KETelIccAdnPhoneBook);
       
   136     } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameFdn) {
       
   137         storeName.Copy(KETelIccFdnPhoneBook);
       
   138     } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameAdn) {
       
   139         storeName.Copy(KETelIccAdnPhoneBook);
       
   140     } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameSdn) {
       
   141         storeName.Copy(KETelIccSdnPhoneBook);
       
   142     }
       
   143 
       
   144     // Check that we got a valid store name
       
   145     if(storeName.Length() == 0) {
       
   146         User::Leave(KErrArgument);
       
   147     }
       
   148 }
       
   149 
       
   150 bool CntSimStorePrivate::read(int index, int numSlots, QContactManager::Error *error)
       
   151 {
       
   152     if (IsActive()) {
       
   153         *error = QContactManager::LockedError;
       
   154         return false;
       
   155     }
       
   156     
       
   157     // start reading
       
   158     m_buffer.Zero();
       
   159     m_buffer.ReAlloc(KOneSimContactBufferSize*numSlots);
       
   160     m_etelStore.Read(iStatus, index, numSlots, m_buffer);
       
   161     SetActive();
       
   162     m_state = ReadState;
       
   163     
       
   164     *error = QContactManager::NoError;
       
   165     return true;
       
   166 }
       
   167 
       
   168 bool CntSimStorePrivate::write(const QContact &contact, QContactManager::Error *error)
       
   169 {
       
   170     if (IsActive()) {
       
   171         *error = QContactManager::LockedError;
       
   172         return false;
       
   173     }
       
   174     
       
   175     // get index
       
   176     m_writeIndex = KErrNotFound;
       
   177     if (contact.id().managerUri() == m_managerUri &&
       
   178         contact.localId() > 0) {
       
   179         m_writeIndex = contact.localId();
       
   180 
       
   181         // TODO: check that the contact exist in the sim 
       
   182     }
       
   183     
       
   184     // encode
       
   185     m_buffer.Zero();
       
   186     m_buffer.ReAlloc(KOneSimContactBufferSize);
       
   187     m_convertedContact = QContact(contact);
       
   188 
       
   189     TRAPD(err, encodeSimContactL(&m_convertedContact, m_buffer));
       
   190     if (err != KErrNone) {
       
   191         CntSymbianSimTransformError::transformError(err, error);
       
   192         return false;
       
   193     }
       
   194 
       
   195     // start writing
       
   196     m_etelStore.Write(iStatus, m_buffer, m_writeIndex);
       
   197     SetActive();
       
   198     m_state = WriteState;
       
   199     
       
   200     *error = QContactManager::NoError;
       
   201     return true;
       
   202 }
       
   203 
       
   204 bool CntSimStorePrivate::remove(int index, QContactManager::Error *error)
       
   205 {
       
   206     if (IsActive()) {
       
   207         *error = QContactManager::LockedError;
       
   208         return false;
       
   209     }
       
   210     
       
   211     // NOTE:
       
   212     // If index points to an empty slot and running in hardware the 
       
   213     // delete operation will not return any error.
       
   214     
       
   215     m_etelStore.Delete(iStatus, index);
       
   216     SetActive();
       
   217     m_state = DeleteState;
       
   218     
       
   219     *error = QContactManager::NoError;
       
   220     return true;
       
   221 }
       
   222 
       
   223 bool CntSimStorePrivate::getReservedSlots(QContactManager::Error *error)
       
   224 {
       
   225     if (IsActive()) {
       
   226         *error = QContactManager::LockedError;
       
   227         return false;
       
   228     }
       
   229     
       
   230     // start reading
       
   231     m_buffer.Zero();
       
   232     m_buffer.ReAlloc(KOneSimContactBufferSize*m_storeInfo.m_totalEntries);
       
   233     m_etelStore.Read(iStatus, 1, m_storeInfo.m_totalEntries, m_buffer);
       
   234     SetActive();
       
   235     m_state = ReadReservedSlotsState;
       
   236     
       
   237     *error = QContactManager::NoError;
       
   238     return true;  
       
   239 }
       
   240 
       
   241 void CntSimStorePrivate::DoCancel()
       
   242 {
       
   243     if (m_state == ReadState)
       
   244         m_etelStore.CancelAsyncRequest(EMobilePhoneStoreRead);
       
   245     if (m_state == WriteState)
       
   246         m_etelStore.CancelAsyncRequest(EMobilePhoneStoreWrite);
       
   247     if (m_state == DeleteState)
       
   248         m_etelStore.CancelAsyncRequest(EMobilePhoneStoreDelete);
       
   249     if (m_state == ReadReservedSlotsState)
       
   250         m_etelStore.CancelAsyncRequest(EMobilePhoneStoreRead);
       
   251     
       
   252     m_state = InactiveState;
       
   253 }
       
   254 
       
   255 void CntSimStorePrivate::RunL()
       
   256 {
       
   257     //qDebug() << "CntSimStorePrivate::RunL()" << m_state << iStatus.Int();
       
   258     
       
   259     m_asyncError = iStatus.Int();
       
   260     User::LeaveIfError(iStatus.Int());
       
   261     
       
   262     // NOTE: It is assumed that emitting signals is queued
       
   263     
       
   264     switch (m_state)
       
   265     {
       
   266         case ReadState:
       
   267         {
       
   268             QList<QContact> contacts = decodeSimContactsL(m_buffer);
       
   269 
       
   270             // set sync target and read only access constraint and display label
       
   271             QList<QContact>::iterator i;
       
   272             for (i = contacts.begin(); i != contacts.end(); ++i) {
       
   273                 QContactSyncTarget syncTarget;
       
   274                 syncTarget.setSyncTarget(KSimSyncTarget);
       
   275                 m_engine.setReadOnlyAccessConstraint(&syncTarget);
       
   276                 i->saveDetail(&syncTarget);
       
   277                 QContactType contactType = i->detail(QContactType::DefinitionName);
       
   278                 m_engine.setReadOnlyAccessConstraint(&contactType);
       
   279                 i->saveDetail(&contactType);
       
   280                 m_engine.updateDisplayLabel(*i);
       
   281             }
       
   282             
       
   283             emit m_simStore.readComplete(contacts, QContactManager::NoError);
       
   284         }
       
   285         break;
       
   286         
       
   287         case WriteState:
       
   288         {
       
   289             // save id
       
   290             QContactId contactId;
       
   291             contactId.setLocalId(m_writeIndex);
       
   292             contactId.setManagerUri(m_managerUri);
       
   293             m_convertedContact.setId(contactId);  
       
   294             
       
   295             // set sync target
       
   296             if(m_convertedContact.detail(QContactSyncTarget::DefinitionName).isEmpty()) {
       
   297                 QContactSyncTarget syncTarget = m_convertedContact.detail(QContactSyncTarget::DefinitionName);
       
   298                 syncTarget.setSyncTarget(KSimSyncTarget);
       
   299                 m_engine.setReadOnlyAccessConstraint(&syncTarget);
       
   300                 m_convertedContact.saveDetail(&syncTarget);
       
   301             }
       
   302 
       
   303             // set type as read only
       
   304             QContactType contactType = m_convertedContact.detail(QContactType::DefinitionName);
       
   305             m_engine.setReadOnlyAccessConstraint(&contactType);
       
   306             m_convertedContact.saveDetail(&contactType);
       
   307 
       
   308             emit m_simStore.writeComplete(m_convertedContact, QContactManager::NoError);
       
   309         }
       
   310         break;
       
   311         
       
   312         case DeleteState:
       
   313         {
       
   314             emit m_simStore.removeComplete(QContactManager::NoError);
       
   315         }
       
   316         break;
       
   317         
       
   318         case ReadReservedSlotsState:
       
   319         {
       
   320             QList<int> reservedSlots = decodeReservedSlotsL(m_buffer);
       
   321             emit m_simStore.getReservedSlotsComplete(reservedSlots, QContactManager::NoError);
       
   322         }
       
   323         break;
       
   324         
       
   325         default:
       
   326         {
       
   327             User::Leave(KErrUnknown);
       
   328         }
       
   329         break;
       
   330     }
       
   331     m_state = InactiveState;
       
   332 }
       
   333 
       
   334 TInt CntSimStorePrivate::RunError(TInt aError)
       
   335 {
       
   336     QContactManager::Error qtError = QContactManager::NoError;
       
   337     CntSymbianSimTransformError::transformError(aError, &qtError);
       
   338     
       
   339     // NOTE: It is assumed that emitting signals is queued
       
   340         
       
   341     if (m_state == ReadState) 
       
   342         emit m_simStore.readComplete(QList<QContact>(), qtError);            
       
   343     else if (m_state == WriteState)
       
   344         emit m_simStore.writeComplete(m_convertedContact, qtError);  
       
   345      else if (m_state == DeleteState)
       
   346         emit m_simStore.removeComplete(qtError);
       
   347     else if (m_state == ReadReservedSlotsState)
       
   348         emit m_simStore.getReservedSlotsComplete(QList<int>(), qtError);
       
   349         
       
   350     m_state = InactiveState;
       
   351     
       
   352     return KErrNone;
       
   353 }
       
   354 
       
   355 /*! Parses SIM contacts in TLV format.
       
   356  *
       
   357  * \param rawData SIM contacts in TLV format.
       
   358  * \return List of contacts.
       
   359 */
       
   360 QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const
       
   361 {
       
   362     PbkPrintToLog(_L("CntSymbianSimEngine::decodeSimContactsL() - IN"));
       
   363     QList<QContact> fetchedContacts;
       
   364     QContact currentContact;
       
   365 
       
   366     TBuf16<KDataClientBuf> buffer;
       
   367     TPtrC16 bufPtr(buffer);
       
   368 
       
   369     TUint8 tagValue(0);
       
   370     CPhoneBookBuffer::TPhBkTagType dataType;
       
   371 
       
   372     bool isAdditionalNumber = false;
       
   373 
       
   374     CPhoneBookBuffer* pbBuffer = new(ELeave) CPhoneBookBuffer();
       
   375     CleanupStack::PushL(pbBuffer);
       
   376     pbBuffer->Set(&rawData);
       
   377     pbBuffer->StartRead();
       
   378 
       
   379     while (pbBuffer->GetTagAndType(tagValue, dataType) == KErrNone) {
       
   380         switch (tagValue)
       
   381         {
       
   382             case RMobilePhoneBookStore::ETagPBAdnIndex:
       
   383             {
       
   384                 //save contact's id (SIM card index) and manager's name
       
   385                 TUint16  index;
       
   386                 if (pbBuffer->GetValue(index) == KErrNone) {
       
   387                     QScopedPointer<QContactId> contactId(new QContactId());
       
   388                     contactId->setLocalId(index);
       
   389                     contactId->setManagerUri(m_managerUri);
       
   390                     currentContact.setId(*contactId);
       
   391                 }
       
   392                 isAdditionalNumber = false;
       
   393                 break;
       
   394             }
       
   395             case RMobilePhoneBookStore::ETagPBTonNpi:
       
   396             {
       
   397                 // Note, that TON info can be incorporated into the phone number by Etel
       
   398                 // implementation (TSY). E.g. this is the case with Nokia TSY.
       
   399                 // Here general case is implemented.
       
   400 
       
   401                 // Check number type, we are only interested if it's international or not.
       
   402                 // We assume here that ETagPBTonNpi always comes after ETagPBNumber, not before.
       
   403                 TUint8  tonNpi;
       
   404                 if (pbBuffer->GetValue(tonNpi) == KErrNone) {
       
   405                     TUint8  intFlag = (tonNpi & KEtsiTonPosition) >> 4;
       
   406                     if (intFlag == 1) {
       
   407                         //international number format, append "+" to the last
       
   408                         //saved number
       
   409                         QList<QContactDetail> phoneNumbers = currentContact.details(
       
   410                                 QContactPhoneNumber::DefinitionName);
       
   411                         if (phoneNumbers.count() > 0) {
       
   412                             QContactPhoneNumber lastNumber = static_cast<QContactPhoneNumber>(
       
   413                                 phoneNumbers.at(phoneNumbers.count() - 1));
       
   414                             QString number = lastNumber.number();
       
   415                             number.insert(0, "+");
       
   416                             lastNumber.setNumber(number);
       
   417                             if (m_storeInfo.m_readOnlyAccess)
       
   418                                 m_engine.setReadOnlyAccessConstraint(&lastNumber);
       
   419                             currentContact.saveDetail(&lastNumber);
       
   420                         }
       
   421                     }
       
   422                 }
       
   423 
       
   424                 // We have rearched to the end of the number,
       
   425                 // invalidate additional number flag.
       
   426                 isAdditionalNumber = false;
       
   427                 break;
       
   428             }
       
   429             case RMobilePhoneBookStore::ETagPBText:
       
   430             {
       
   431                 if (pbBuffer->GetValue(bufPtr) == KErrNone) {
       
   432                     if (isAdditionalNumber) {
       
   433                         // For additional number bufPtr contains number alpha string,
       
   434                         // this is ignored currently
       
   435                     }
       
   436                     else {
       
   437                         // Contact name otherwise
       
   438                         QContactName name;
       
   439                         QString nameString = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length());
       
   440                         name.setCustomLabel(nameString);
       
   441                         if (m_storeInfo.m_readOnlyAccess)
       
   442                             m_engine.setReadOnlyAccessConstraint(&name);
       
   443                         currentContact.saveDetail(&name);
       
   444                         QContactManager::Error error(QContactManager::NoError);
       
   445                         m_engine.setContactDisplayLabel(&currentContact, m_engine.synthesizedDisplayLabel(currentContact, &error));
       
   446                     }
       
   447                 }
       
   448                 break;
       
   449             }
       
   450             case RMobilePhoneBookStore::ETagPBSecondName:
       
   451             {
       
   452                 if (pbBuffer->GetValue(bufPtr) == KErrNone) {
       
   453                     QContactNickname nickName;
       
   454                     QString name = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length());
       
   455                     nickName.setNickname(name);
       
   456                     if (m_storeInfo.m_readOnlyAccess)
       
   457                         m_engine.setReadOnlyAccessConstraint(&nickName);
       
   458                     currentContact.saveDetail(&nickName);
       
   459                 }
       
   460                 break;
       
   461             }
       
   462             case RMobilePhoneBookStore::ETagPBNumber:
       
   463             {
       
   464                 if (pbBuffer->GetValue(bufPtr) == KErrNone) {
       
   465                     QContactPhoneNumber phoneNumber;
       
   466                     QString number = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length());
       
   467                     phoneNumber.setNumber(number);
       
   468                     if (m_storeInfo.m_readOnlyAccess)
       
   469                         m_engine.setReadOnlyAccessConstraint(&phoneNumber);
       
   470                     currentContact.saveDetail(&phoneNumber);
       
   471                 }
       
   472                 break;
       
   473             }
       
   474             case RMobilePhoneBookStore::ETagPBAnrStart:
       
   475             {
       
   476                 // This tag should precede every additional number entry
       
   477                 isAdditionalNumber = true;
       
   478                 break;
       
   479             }
       
   480             case RMobilePhoneBookStore::ETagPBEmailAddress:
       
   481             {
       
   482                 if (pbBuffer->GetValue(bufPtr) == KErrNone) {
       
   483                     QContactEmailAddress email;
       
   484                     QString emailAddress = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length());
       
   485                     email.setEmailAddress(emailAddress);
       
   486                     if (m_storeInfo.m_readOnlyAccess)
       
   487                         m_engine.setReadOnlyAccessConstraint(&email);
       
   488                     currentContact.saveDetail(&email);
       
   489                 }
       
   490                 break;
       
   491             }
       
   492             case RMobilePhoneBookStore::ETagPBNewEntry:
       
   493             {
       
   494                 // This signals the end of the entry and is a special case
       
   495                 // which will be handled below.
       
   496                 break;
       
   497             }
       
   498             default:
       
   499             {
       
   500                 // An unsupported field type - just skip this value
       
   501                 pbBuffer->SkipValue(dataType);
       
   502                 break;
       
   503             }
       
   504         } //switch
       
   505 
       
   506         // save contact to the array of contact to be returned if the whole entry was extracted
       
   507         if ((tagValue == RMobilePhoneBookStore::ETagPBNewEntry && currentContact.localId() > 0) ||
       
   508             (pbBuffer->RemainingReadLength() == 0 && currentContact.localId() > 0)) {
       
   509             fetchedContacts.append(currentContact);
       
   510             //clear current contact
       
   511             currentContact.clearDetails();
       
   512             QScopedPointer<QContactId> contactId(new QContactId());
       
   513             contactId->setLocalId(0);
       
   514             contactId->setManagerUri(QString());
       
   515             currentContact.setId(*contactId);
       
   516         }
       
   517     } //while
       
   518 
       
   519     CleanupStack::PopAndDestroy(pbBuffer);
       
   520     PbkPrintToLog(_L("CntSymbianSimEngine::decodeSimContactsL() - OUT"));
       
   521     return fetchedContacts;
       
   522 }
       
   523 
       
   524 /*! Converts QContact to the TLV format which is used to save it to the Etel store.
       
   525  *
       
   526  * \param contact QContact to be converted
       
   527  * \param rawData Contact in TLV format on return.
       
   528 */
       
   529 void CntSimStorePrivate::encodeSimContactL(QContact* contact, TDes8& rawData) const
       
   530 {
       
   531     // Keep track of the count of phone numbers added
       
   532     int phoneNumberCount(0);
       
   533 
       
   534     // Create buffer
       
   535     CPhoneBookBuffer* pbBuffer = new(ELeave) CPhoneBookBuffer();
       
   536     CleanupStack::PushL(pbBuffer);
       
   537     pbBuffer->Set(&rawData);
       
   538     User::LeaveIfError(pbBuffer->AddNewEntryTag());
       
   539 
       
   540     // Loop through details
       
   541     foreach(QContactDetail detail, contact->details()) 
       
   542     {
       
   543         QString definitionName = detail.definitionName();
       
   544         
       
   545         // NOTE: If the detail is too long let the etel store return
       
   546         // an error about it. We could check the maximum lenghts for each
       
   547         // detail but then we would need to read it before every write operation
       
   548         // bacause it seems to change depending on how full the sim card is.
       
   549 
       
   550         // Name
       
   551         if (definitionName == QContactName::DefinitionName) 
       
   552         {
       
   553             QContactName nameDetail = static_cast<QContactName>(detail);
       
   554             QString name = nameDetail.customLabel();
       
   555             putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBText, name);
       
   556         }
       
   557         // Phone number
       
   558         else if (definitionName == QContactPhoneNumber::DefinitionName)
       
   559         {
       
   560             if (m_storeInfo.m_additionalNumberSupported == false && phoneNumberCount>0)
       
   561                 User::Leave(KErrNotSupported);
       
   562 
       
   563             phoneNumberCount++;
       
   564             QString number = static_cast<QContactPhoneNumber>(detail).number();
       
   565             if (number.isEmpty())
       
   566                 continue;
       
   567 
       
   568             // Verify the number only contains legal digits
       
   569             foreach (const QChar character, number) {
       
   570                 if(!character.isDigit()) {
       
   571                     if(character != QChar('+')
       
   572                         && character != QChar('*')
       
   573                         && character != QChar('#')
       
   574                         && character != QChar('p')
       
   575                         && character != QChar('w')) {
       
   576                         User::Leave(KErrArgument);
       
   577                     }
       
   578                 }
       
   579             }
       
   580 
       
   581             if (phoneNumberCount > 1) {
       
   582                 // Mark the beginning of an additional number
       
   583                 User::LeaveIfError(pbBuffer->AddNewNumberTag());
       
   584             }
       
   585 
       
   586             // The number itself
       
   587             putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBNumber, number);
       
   588         }
       
   589         // Nickname
       
   590         else if (definitionName == QContactNickname::DefinitionName)
       
   591         {
       
   592             if (m_storeInfo.m_secondNameSupported == false)
       
   593                 User::Leave(KErrNotSupported);
       
   594             
       
   595             QString nickname = static_cast<QContactNickname>(detail).nickname();
       
   596             if (nickname.isEmpty())
       
   597                 continue;
       
   598             
       
   599             putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBSecondName, nickname);
       
   600         } 
       
   601         // email
       
   602         else if (definitionName == QContactEmailAddress::DefinitionName)
       
   603         {
       
   604             if (m_storeInfo.m_emailSupported == false)
       
   605                 User::Leave(KErrNotSupported);
       
   606             
       
   607             QString email = static_cast<QContactEmailAddress>(detail).emailAddress();
       
   608             if (email.isEmpty())
       
   609                 continue;            
       
   610             
       
   611             putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBEmailAddress, email);
       
   612         }
       
   613         // These are ignored in the conversion
       
   614         else if (definitionName == QContactSyncTarget::DefinitionName
       
   615             || definitionName == QContactDisplayLabel::DefinitionName
       
   616             || definitionName == QContactType::DefinitionName) 
       
   617         {
       
   618             // Do nothing
       
   619         } 
       
   620         else 
       
   621         {
       
   622             // Unknown detail
       
   623             User::Leave(KErrArgument);
       
   624         }
       
   625     }
       
   626     CleanupStack::PopAndDestroy(pbBuffer);
       
   627 }
       
   628 
       
   629 void CntSimStorePrivate::putTagAndValueL(CPhoneBookBuffer* pbBuffer, TUint8 tag, QString data) const
       
   630 {
       
   631     TPtrC value(reinterpret_cast<const TUint16*>(data.utf16()));
       
   632     User::LeaveIfError(pbBuffer->PutTagAndValue(tag, value));
       
   633 }
       
   634 
       
   635 QList<int> CntSimStorePrivate::decodeReservedSlotsL(TDes8& rawData) const
       
   636 {
       
   637     QList<int> reservedSlots;
       
   638 
       
   639     TUint8 tagValue(0);
       
   640     CPhoneBookBuffer::TPhBkTagType dataType;
       
   641 
       
   642     CPhoneBookBuffer* pbBuffer = new (ELeave) CPhoneBookBuffer();
       
   643     CleanupStack::PushL(pbBuffer);
       
   644     pbBuffer->Set(&rawData);
       
   645     pbBuffer->StartRead();
       
   646 
       
   647     while (pbBuffer->GetTagAndType(tagValue, dataType) == KErrNone)
       
   648     {
       
   649         if (tagValue == RMobilePhoneBookStore::ETagPBAdnIndex) 
       
   650         {
       
   651             TUint16 index;
       
   652             if (pbBuffer->GetValue(index) == KErrNone)
       
   653                 reservedSlots.append(index);
       
   654         } else
       
   655             pbBuffer->SkipValue(dataType);
       
   656     } //while
       
   657     
       
   658     CleanupStack::PopAndDestroy(pbBuffer);
       
   659     return reservedSlots;
       
   660 }
       
   661 
       
   662 void CntSimStorePrivate::writeL(QContact *contact)
       
   663 {
       
   664     if (IsActive())
       
   665         User::Leave(KErrLocked);
       
   666     
       
   667     // get index
       
   668     int index = KErrNotFound;
       
   669     if (contact->id().managerUri() == m_managerUri &&
       
   670         contact->localId() > 0) {
       
   671         index = contact->localId();
       
   672     }
       
   673     
       
   674     // encode
       
   675     m_buffer.Zero();
       
   676     m_buffer.ReAlloc(KOneSimContactBufferSize);
       
   677     encodeSimContactL(contact, m_buffer);
       
   678 
       
   679     // write
       
   680     TRequestStatus status;
       
   681     m_etelStore.Write(status, m_buffer, index);
       
   682     User::WaitForRequest(status);
       
   683     User::LeaveIfError(status.Int());
       
   684     
       
   685     // update id
       
   686     QContactId id;
       
   687     id.setLocalId(index);
       
   688     id.setManagerUri(m_managerUri);
       
   689     contact->setId(id);
       
   690 }
       
   691 
       
   692 void CntSimStorePrivate::removeL(int index)
       
   693 {
       
   694     if (IsActive())
       
   695         User::Leave(KErrLocked);
       
   696     
       
   697     // NOTE:
       
   698     // If index points to an empty slot and running in hardware the 
       
   699     // delete operation will not return any error.
       
   700     
       
   701     TRequestStatus status;
       
   702     m_etelStore.Delete(status, index);
       
   703     User::WaitForRequest(status);
       
   704     User::LeaveIfError(status.Int());
       
   705 }
       
   706 
       
   707 void CntSimStorePrivate::updateStoreInfoL()
       
   708 {
       
   709 #ifdef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1
       
   710     RMobilePhoneBookStore::TMobilePhoneBookInfoV1 info;
       
   711     RMobilePhoneBookStore::TMobilePhoneBookInfoV1Pckg infoPckg(info);
       
   712 #else
       
   713     RMobilePhoneBookStore::TMobilePhoneBookInfoV5 info;
       
   714     RMobilePhoneBookStore::TMobilePhoneBookInfoV5Pckg infoPckg(info);
       
   715 #endif
       
   716 
       
   717     // Get info
       
   718     TRequestStatus status;
       
   719     m_etelStore.GetInfo(status, infoPckg);
       
   720     User::WaitForRequest(status);
       
   721     User::LeaveIfError(status.Int());
       
   722 
       
   723     // Update entry counts
       
   724     m_storeInfo.m_totalEntries = info.iTotalEntries;
       
   725     m_storeInfo.m_usedEntries  = info.iUsedEntries;
       
   726     
       
   727 #ifdef SYMBIANSIM_BACKEND_TEST_EXTRADETAILS
       
   728     // Check if store supports the extra details
       
   729     //
       
   730     // NOTE: 
       
   731     // We cannot rely on TMobilePhoneBookInfoV5 to check if we support
       
   732     // these details. For example iMaxSecondNames is allways -1 even if the sim
       
   733     // card supports a second name.
       
   734     //
       
   735     // There is an API for checking these but it's Nokia internal so we must
       
   736     // do it this way - by checking if saving these details is possible.
       
   737     
       
   738     // Have we checked these already?
       
   739     if (m_extraDetailsChecked == false)
       
   740     {
       
   741         // Cannot test extra details if sim card is full 
       
   742         if (m_storeInfo.m_usedEntries == m_storeInfo.m_totalEntries)
       
   743             return;
       
   744 
       
   745         // Cancel store event listener temporarily
       
   746         if (m_listener)
       
   747             m_listener->Cancel();
       
   748 
       
   749         // Test writing nickname
       
   750         QContact contact;
       
   751         QContactNickname nick;
       
   752         nick.setNickname("simbackend test");
       
   753         contact.saveDetail(&nick);
       
   754         TRAPD(err, {
       
   755             m_storeInfo.m_secondNameSupported = true; // enable to pass encodeSimContactL()
       
   756             writeL(&contact);
       
   757             removeL(contact.localId());
       
   758         } );
       
   759         if (err)
       
   760             m_storeInfo.m_secondNameSupported = false;
       
   761 
       
   762         // Test writing additional number
       
   763         contact = QContact();
       
   764         QContactPhoneNumber num1;
       
   765         num1.setNumber("1111111111");
       
   766         contact.saveDetail(&num1);
       
   767         QContactPhoneNumber num2;
       
   768         num2.setNumber("2222222222");
       
   769         contact.saveDetail(&num2);
       
   770         TRAP(err, {
       
   771             m_storeInfo.m_additionalNumberSupported = true; // enable to pass encodeSimContactL()
       
   772             writeL(&contact); 
       
   773             removeL(contact.localId()); 
       
   774         } );
       
   775         if (err)
       
   776             m_storeInfo.m_additionalNumberSupported = false;
       
   777 
       
   778         // Test writing email
       
   779         contact = QContact();
       
   780         QContactEmailAddress email;
       
   781         email.setEmailAddress("simbackend@test.com");
       
   782         contact.saveDetail(&email);
       
   783         TRAP(err, {
       
   784             m_storeInfo.m_emailSupported = true; // enable to pass encodeSimContactL()
       
   785             writeL(&contact); 
       
   786             removeL(contact.localId()); 
       
   787         } );
       
   788         if (err)
       
   789             m_storeInfo.m_emailSupported = false;
       
   790 
       
   791         // Start store event listener again
       
   792         if (m_listener)
       
   793             m_listener->start();
       
   794 
       
   795         m_extraDetailsChecked = true;
       
   796     }
       
   797 #endif
       
   798 
       
   799     /*
       
   800     qDebug() << "Store info:"
       
   801         << "\nStore name                :" << m_storeInfo.m_storeName
       
   802         << "\nTotal entries             :" << m_storeInfo.m_totalEntries
       
   803         << "\nUsed entries              :" << m_storeInfo.m_usedEntries
       
   804         << "\nRead only access          :" << m_storeInfo.m_readOnlyAccess
       
   805         << "\nNumber supported          :" << m_storeInfo.m_numberSupported
       
   806         << "\nName supported            :" << m_storeInfo.m_nameSupported
       
   807         << "\nSecond name supported     :" << m_storeInfo.m_secondNameSupported
       
   808         << "\nAdditional name supported :" << m_storeInfo.m_additionalNumberSupported
       
   809         << "\nEmail supported           :" << m_storeInfo.m_emailSupported;
       
   810     */
       
   811 }