如何获得Symbian手机中的通讯录

本文分析中有网易博客中摘录,病毒可能也会利用下面功能获取用户隐私.

 

1.电话簿API也叫Contacts API,是SymbianOS的通信簿API在Series60上的实现.大部分通信簿引擎API与电话簿API是
重复的,只有在电话簿API不能满足需要时才使用通信簿API.

2.Symbian OS手机的通讯录采用文件方式存储。 每个Symbian OS手机都有一个默认的通讯录数据库,在os7.0s中存
在为c:\system\data\Contacts.cdb,在os9.0s中存在为c:\private\100012a5\DBS_100065FF_Contacts.cdb。

3.Symbian OS的手机通讯录在开发上的操作依靠Symbian OS通讯录模型(Contacts Model)实现。

    通讯录模型由通讯录数据库、通讯录条目(项)和通信录域三者组成.

    他们之间的关系是:一个手机除了系统自带的默认通讯录数据库外,还可以带多个通讯录数据库;一个通讯录数
据库有多个通讯录条目组成,每个条目就是每个联系人,具体数量限制各个手机应该不一样;而一个通讯录条目又
由多个通讯录域组成,如姓名、工作手机号码、家庭手机号码等等,每个项就是一个域。

4.Symbian OS将Contacts Model操作封装为几个类,主要有:

   (1).CContactDatabase(通讯录数据库类):除了负责新建、打开、关闭等基本数据库操作外,还负责数据库更
新(通讯录条目的新建、修改、删除需要通过CContactDatabase类的操作才能实现)、排序和查找,另外还有一些
建立快速拨号之类的操作也是通过它来实现。

   (2).CContactItem(通讯录条目类):由唯一的一个TContactItemId(typedef TInt32 TContactItemId)标识
,负责具体一个通讯录条目的创建、修改,其直接管理每一个通讯录域

   (3).CContactItemField(域类):每一个域就是一个真实单一的数据,该数据的类型由存储类型(TStorageTy
pe)和域类型(TFieldType)同时决定,具体的四种存储类型和多种域类型定义见系统头文件cntdef.h内的定义。

   (4).还有很多其它的类,如CContactItemFieldSet(域集类)、CContactFieldStorage(与存储基类)、CCont
actTextFields(文本存储域类)、MContactDbObserver(通讯数据库观察类)等.

   (5).使用这些类需要包含一些头文件,如

       #include <CNTDB.H> //for CContactDatabase
       #include <CNTITEM.H> //for CContactItem

       #include <CNTFIELD.H>//for CContactItemField
       #include <CNTFLDST.H>//for CContactTextField
       #include <CNTFILT.H> //for CCntFilter

       在.mmp文件中添加 LIBRARY cntmodel.lib

       另外需要添加一些能力: ReadUserData,WriteUserData

5.以下是对通讯录数据库的一些基本的操作:

(1).   打开和关闭数据库

     CContactDatabase::OpenL()函数有两个重载函数。如果该函数没有给出一个参数,就打开默认的数据库。另
一种情况是,可以传递一个有关数据库的路径和文件名,规定打开一个指定数据库,请参考sdk文档。

    //打开默认数据库

    CContactDatabase* contactsDb = CContactDatabase::OpenL();

    CleanupStack::PushL(contactsDb);

    //取得当前数据库所有通讯条目数

    TInt numberOfContacts = contactsDb->CountL();

    //释放数据库

    CleanupStack::PopAndDestroy(contactsDb);

    需要注意:通信录数据库并不具有Close()函数或类似的函数,否则压入清除栈时就得用CleanupClosePushL(
)函数了.

(2). 创建数据库

    CContactDatabase::CreateL()函数,如果该数据库已经存在,会以KErrAlreadyExists退出。而CContactDatab
ase::ReplaceL()函数结果总会创建或替换一个数据库,如果没有定义参数,这些函数将创建一个默认的数据库。C
ContactDatabase::FindContactFile()函数接受一个描述符,如果不存在默认数据库的话,该描述符就会返回该默
认数据库的位置。在第三版中,这个函数的作用被ContactDatabaseExistsL()替代,它接受的参数类型是const的.

_LIT(KDbFileName,"c:newdatabase.cdb");

CContactDatabase* newDefaultContactDb;

//是否存在默认数据库
if(CContactDatabase::ContactDatabaseExistsL(KDbFileName)) //FindContactFile
{
newDefaultContactDb = CContactDatabase::ReplaceL(KDbFileName);
}
else
{
newDefaultContactDb = CContactDatabase::CreateL(KDbFileName);
}

CleanupStack::PushL(newDefaultContactDb);

// 添加自己功能代码
CleanupStack::PopAndDestroy(newDefaultContactDb);

结果在第三版模拟器运行时,会在\Epoc32\winscw\c\private\100012a5中创建DBS_100065FF_newdatabase.cdb

(3). 添加通讯录条目

    CContactDatabase* contactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactsDb);

// 字符串声明
_LIT(KForenameLabel,"Forename");//三个标签
_LIT(KSurnameLabel,"Surname");

_LIT(KWorkPhoneLabel,"Work Phone");
_LIT(KForename,"Steve");//名,姓,电话号码
_LIT(KSurname,"Wilkinson");
_LIT(KWorkPhone,"+441617779700");

// 建立一个新条目
CContactItem* contact = CContactCard::NewLC();//CContactCard继续自CContactItem

//创建一个新的文本存储类型的familyname域
CContactItemField* field

= CContactItemField::NewLC(KStorageTypeText, KUidContactFieldFamilyName);
//将姓域建立与vCard的映射
field->SetMapping(KUidContactFieldVCardMapUnusedN);
//设置域标签
field->SetLabelL(KSurnameLabel);
//设置域值
field->TextStorage()->SetTextL(KSurname);
//把该域加入到新建的条目中
contact->AddFieldL(*field);
CleanupStack::Pop();//*field的控制权交给了contact

//添加文本存储类型的名域
field=CContactItemField::NewLC(KStorageTypeText, KUidContactFieldGivenName);
field->SetMapping(KUidContactFieldVCardMapUnusedN);
field->SetLabelL(KForenameLabel);
field->TextStorage()->SetTextL(KForename);
contact->AddFieldL(*field);
CleanupStack::Pop();

//添加文本存储类型的手机号码域
field=CContactItemField::NewLC(KStorageTypeText, KUidContactFieldPhoneNumber);
field->SetMapping(KUidContactFieldVCardMapTEL);
field->SetLabelL(KWorkPhoneLabel);
field->TextStorage()->SetTextL(KWorkPhone);
contact->AddFieldL(*field);
CleanupStack::Pop();

//把建立的新记录添加到数据库中
contactsDb->AddNewContactL(*contact);
contactsDb->SetOwnCardL(*contact);

CleanupStack::PopAndDestroy(contact);

//释放数据库
CleanupStack::PopAndDestroy(contactsDb);

(4). 读取(遍历)通讯录条目

    使用TContactIter类(该类起到数据库操作中类似游标的作用)来遍历一个通信录数据库。它提供了一整套的
函数,用于遍历所有的通信录项。所有的函数都用通信录项ID (TContactItemId) 进行操作,该ID 用于访问某个特
定的通信录项。

CContactDatabase* contactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactsDb);


TContactIter iter(*contactsDb);
TContactItemId cardId;

RFileLogger log;
log.Connect();
log.CreateLog(_L("Log"),_L("Log.txt"),EFileLoggingModeAppend);

//The count includes non-system template items
TInt count=contactsDb->CountL();
TInt templateCount=contactsDb->TemplateCount();
log.WriteFormat(_L("count:%d"),count);
log.WriteFormat(_L("templateCount:%d"),templateCount);


//循环遍历
while( ( cardId = iter.NextL() ) != KNullContactId )
{
//读取相应项,这里之所以称其card,就是其实际相当于读一个完整的vCard条目
CContactItem* card = contactsDb->ReadContactL(cardId);
CleanupStack::PushL(card);

log.WriteFormat(_L("TContactItemId:%d"),cardId);

//判断如果不是模板
if (KErrNotFound==contactsDb->GetCardTemplateIdListL()->Find(card->Id()))
{
   CContactItemFieldSet& fields=card->CardFields();
   TInt fieldCount=fields.Count();
   for (TInt i=0;i<fieldCount;i++)
   {
    CContactItemField& field=fields[i];

    //打印标签和值
    log.Write(field.Label());
    log.Write(field.TextStorage()->Text());
   }

   contactsDb->CloseContactL(card->Id());
   CleanupStack::PopAndDestroy(); // card
}
}

log.CloseLog();
log.Close();

CleanupStack::PopAndDestroy();

(5). 查找并更新通讯录条目

   本实例采用查找函数FindAsyncL,该函数声明为:

   CIdleFinder * CContactDatabase::FindAsyncL(const TDesC &aText, const CContactItemFieldDef *aField
Def, MIdleFindObserver *aObserver);

   为了实现回调,把CContactDatabase* iContactsDb;CIdleFinder * iFinder; 定义为类的成员变量.

void CContactEngine::FindContactL(MIdleFindObserver* aObserver)
{
     iContactsDb = CContactDatabase::OpenL();


     CContactItemFieldDef* iFieldDef = new (ELeave)CContactItemFieldDef();
     CleanupStack::PushL(iFieldDef);


     iFieldDef->AppendL(KUidContactFieldGivenName);
     iFieldDef->AppendL(KUidContactFieldFamilyName);


     _LIT(KFindToken, "Wilkinson");
     iFinder = iContactsDb->FindAsyncL( KFindToken, iFieldDef, aObserver);


     CleanupStack::PopAndDestroy();// iFieldDef
}

再由观察者来调用如下函数,实现修改

void CContactEngine::EditContactL()
{
if(iFinder->IsComplete())
{
if(iFinder->Error() == KErrNone)
{
     CContactIdArray* result = iFinder->TakeContactIds();
     CleanupStack:: PushL(result);


     for(TInt i=0; i<result->Count(); i++)
    {
      TContactItemId cardId = (*result)[i];
      CContactItem* ownCard = iContactsDb ->OpenContactL(cardId);
      CleanupStack::PushL(ownCard);


      TInt index = ownCard->CardFields().Find(KUidContactFieldGivenName);
      _LIT(KGivenName,"weike");
      ownCard->CardFields()[index].TextStorage()->SetTextL(KGivenName);


      index = ownCard->CardFields().Find(KUidContactFieldFamilyName);
      _LIT(KFamilyName,"wang");
      ownCard->CardFields()[index].TextStorage()->SetTextL(KFamilyName);


      //提交所做的修改
      iContactsDb ->CommitContactL(*ownCard);
      //如果这里不做更改可以调用CloseContactL直接关闭
      //iContactsDb->CloseContactL(ownCard->Id());
      CleanupStack::PopAndDestroy();// ownCard
    }
    CleanupStack::PopAndDestroy();//result;
}
}
}

(6). 导出所选通讯录条目到文件(vCard)

    使用了CContactDatabase类中ExportSelectedContactsL函数,与前次遍历方法不同,并且在取所有通讯条目前
加了一个过滤器CCntFilter类,例程如下:

     void CContactEngine::ExportContactL(const TDesC& aFileName)
{
RFs fileSession;

//连接文件服务器
User::LeaveIfError(fileSession.Connect());
CleanupClosePushL(fileSession); //1

//打开默认数据库
CContactDatabase* contactDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactDb); //2

//新建过滤器
CCntFilter* filter = CCntFilter::NewLC(); //3
filter->SetContactFilterTypeALL(EFalse);
//按vCard格式导出
filter->SetContactFilterTypeCard(ETrue);

//安装filter
contactDb->FilterDatabaseL(*filter);

//取出满足条件的记录数据项数组
CContactIdArray* exportContact = CContactIdArray::NewL(filter->iIds);
CleanupStack::PushL(exportContact); //4

RFile file;
//新建文件,aFileName是文件名字
file.Replace(fileSession,aFileName,EFileWrite);
CleanupClosePushL(file); //5

//声明文件流
RFileWriteStream outputStream(file);
CleanupClosePushL(outputStream); //6

TUid id;
id.iUid = KVersitEntityUidVCard;

//导出到文件
contactDb->ExportSelectedContactsL(id,*exportContact, outputStream, CContactDatabase::EExcludeUid);
CleanupStack::PopAndDestroy(6);
}

使用时如: iContactEngine->ExportContactL(_L("c:\\mycards.txt"));

6.S60特有的通讯录操作API引擎

nokia专门为S60平台SDK建立了一个操作通讯录的引擎,以及一些封装的类,主要有:

(1).CPbkContactEngine(通讯录引擎类):如果已经存在一个缺省数据库,CPbkContactEngine::NewL()

就连接到该数据库,否则创建该数据库。也可以传入文件名,打开一个指定的通讯录数据库.

    从它的头文件cpbkcontactengine.h可以看出,它是对CContactDatabase和观察器类MContactDbObserver封装了
下并进行了一些优化,简便了我们操作时的一些代码,为此操作起来比较方便。

(2).CPbkContactItem(通讯录条目类):该类头文件是CPbkContactItem.h,主要对通讯录条目类CContactItem的
封装和优化,可以看出很多导出函数都是一致的。

(3).TPbkContactItemField(域类):该类的头文件tpbkcontactitemfield.h。

使用这些类需要的能力有: ReadUserData WriteUserData ReadDeviceData WriteDeviceData

导入库 PbkEng.lib

(4) 新建通讯录条目

    _LIT(KFName,"andy");
_LIT(KLName,"lau");
_LIT(KNumber,"13794417723");

//运用引擎打开默认通讯录
CPbkContactEngine* iPbkContactEngine = CPbkContactEngine::NewL();
CleanupStack::PushL(iPbkContactEngine);//1

//新建一空通信录项
CPbkContactItem* contact = iPbkContactEngine->CreateEmptyContactL();
CleanupStack::PushL(contact); //2

//设置first name 域
TPbkContactItemField* field = contact->FindField(EPbkFieldIdFirstName);
field->TextStorage()->SetTextL(KFName);
//设置last name 域
field = contact->FindField(EPbkFieldIdLastName);
field->TextStorage()->SetTextL(KLName);
//设置手机号码域
field = contact->FindField(EPbkFieldIdPhoneNumberMobile);
field->TextStorage()->SetTextL(KNumber);

//可以添加其他值域
//...

//修改后结果添加到数据库中,并返回这个通信录项的id,该id可以以后使用
TContactItemId Id = iPbkContactEngine->AddNewContactL(*contact);

CleanupStack::PopAndDestroy(2);

(5). 修改通讯录条目

_LIT(number,"13794417723");

TBuf<11> phonenumber(number);

CPbkContactEngine* iPbkContactEngine = CPbkContactEngine::NewL();
CleanupStack::PushL(iPbkContactEngine);//1

//需要提供一个通讯录条目id
TContactItemId id(23);

//这里打开条目后加锁,以防其它客户端打开
CPbkContactItem* contact = iPbkContactEngine->OpenContactLCX(id); //aContactId

//找到需要修改的field
TPbkContactItemField* field = contact->FindField(EPbkFieldIdPhoneNumberMobile);

//设置并确认修改
field->TextStorage()->SetTextL(phonenumber);
iPbkContactEngine ->CommitContactL(*contact);
CleanupStack::PopAndDestroy(3);

posted @ 2012-12-22 22:57  小金马  阅读(244)  评论(0编辑  收藏  举报