|
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(¤tContact, 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 } |