Symbian端彩信讀取初探
上周由于項(xiàng)目需要對(duì)彩信讀取進(jìn)行了預(yù)研,雖然并未涉及彩信的攔截,只是對(duì)Symbian S60手機(jī)收件箱中的彩信和彩信通知的內(nèi)容進(jìn)行提取并分析,但是也算是趟了下之前一直沒有搞過的彩信這渾水,小結(jié)一下。為了體現(xiàn)分析過程的邏輯性,下文并非是由簡(jiǎn)入繁,而是采用由已知到未知的說明過程來進(jìn)行。
彩信和彩信通知的區(qū)別
正如前面博文《Symbian OS中的消息存儲(chǔ)與常用操作》中提及的那樣,手機(jī)中的各種消息都是以數(shù)據(jù)項(xiàng)(Entry)形式供程序操作,鑒于Nokia之前的SDK有眾多API采用非公開的方式,很多東西碰上了只能拼命的試,試通了就算OK了,短信就是這個(gè)樣子的,自從Cxt將發(fā)送短信的代碼調(diào)通后,目前用的代碼基本都是他那份原型。寫了這么多只想說雖然手機(jī)中的各種消息都是以數(shù)據(jù)項(xiàng)形式給出的,但是數(shù)據(jù)項(xiàng)又由文件夾類型、消息類型、附件類型和服務(wù)類型的數(shù)據(jù)項(xiàng),我們所指的消息如短信、彩信和郵箱乃至用戶自定義的一些消息等等都是消息類型的數(shù)據(jù)項(xiàng)即KuidMsvMessageEntry類型。既然消息類型的數(shù)據(jù)項(xiàng)這么多,那么如何區(qū)分呢,這就靠各自不同的Mtm類型,由于SDK沒有系統(tǒng)性闡述,我們雖然已經(jīng)知道短信的類型是KUidMsgTypeSMS
,但是彩信的Mtm類型值到底是什么,它跟彩信通知是否是有區(qū)分的,我們不得而知。
沒辦法只能通過數(shù)據(jù)項(xiàng)的通用操作,對(duì)手機(jī)中已存的彩信和彩信通知進(jìn)行讀取后進(jìn)行分析內(nèi)容,才得知彩信和彩信通知是兩個(gè)完全不同的Mtm類型值,一個(gè)是0x100058E1,另一個(gè)是0x100059C8,后來通過google搜索才知道前者的宏定義類型為KuidMsgTypeMultimedia,后者的宏定義類型為KUidMsgMMSNotification,兩者在mmsconst.h頭文件中有定義。
同樣使用通用的數(shù)據(jù)項(xiàng)操作,我們可以獲取彩信和彩信通知消息數(shù)據(jù)項(xiàng)的消息存儲(chǔ)
CMsvStore和附件情況,兩者的CMsvStore都是有數(shù)據(jù)的(SizeL()函數(shù)返回都是大于0的),但是卻沒有文本體(HasBodyTextL()返回都是Efalse);另外彩信都是有附件的,而彩信通知一般是沒有附件的。
因?yàn)楦郊旧硪彩菙?shù)據(jù)項(xiàng)(KuidMsvAttachmentEntry類型的數(shù)據(jù)項(xiàng)),所以我們可以更進(jìn)一步通過通用的數(shù)據(jù)項(xiàng)操作將彩信的所有附件全部都讀取出來。而附件類型由文件類型、鏈接類型和數(shù)據(jù)項(xiàng)類型三種,通常彩信中的附件都是文件類型的附件,我們用以下代碼來演示將附件拷貝出來
CMsvStore* inboxStore= iSmsMtm->Entry().ReadStoreL();
CleanupStack::PushL(inboxStore);
MMsvAttachmentManager& attachManager = inboxStore->AttachmentManagerL();
TInt attachmentCount = attachManager.AttachmentCount();
for(TInt i=0;i< attachmentCount;i++)
{
CMsvAttachment *attachment = attachManager.GetAttachmentInfoL(i);
CleanupStack::PushL(attachment);
if(type != CMsvAttachment::EMsvMessageEntry)
{
TFileName newPath(_L("c:\\data\\"));
newPath.AppendNum(aMessageId);
newPath.Append(_L("LLF"));
newPath.Append(attachment->AttachmentName());
RFile file = attachManager.GetAttachmentFileL(i);
TInt size(0);
file.Size(size);
HBufC8 *buf = HBufC8::NewLC(size);
TPtr8 ptrBuf(buf->Des());
file.Read(ptrBuf, size);
RFile newFile;
User::LeaveIfError(newFile.Replace(iFs, newPath, EFileWrite));
newFile.Write(ptrBuf);
newFile.Close();
CleanupStack::PopAndDestroy(buf);
}
CleanupStack::PopAndDestroy();
}
CleanupStack::PopAndDestroy();
為了更加直觀的區(qū)分各個(gè)附件隸屬于不同的消息,為此以消息ID打頭并加LLF來區(qū)分附件的各個(gè)文件,一條家庭生活報(bào)的彩信,我獲取到13個(gè)附件的截圖如下
其中有smil文件,有圖片文件,也有文本文件。
使用通用的數(shù)據(jù)項(xiàng)操作方法,借助SDK我們對(duì)彩信和彩信通知的內(nèi)容區(qū)別和內(nèi)容分析只能到這里了。
彩信的內(nèi)容分析
顯然如果僅僅知道以上內(nèi)容還是不夠的,困擾我們的
CMsvStore里面到底有些什么東西呢?暫時(shí)從代碼里面走出去,我們來參閱下Nokia發(fā)布的一份難得的中文文檔《CH_How_To_Create_MMS_Services_v4_0.pdf》(詳見附件),正如短信有短信的PDU一樣,彩信也是有其PDU的,這個(gè)PDU就在這個(gè)文檔的第五部分,在這里就不詳述了,從里面截個(gè)圖過來說明下問題。
由這個(gè)MMS的PDU,我們可以猜想下附件就是Message Body,而
CMsvStore是否就是MMS Header的內(nèi)容呢?
幸好現(xiàn)在Nokia將Symbian開源了,否則這個(gè)猜想是沒有辦法揭開了,通過Symbian網(wǎng)站上的源代碼瀏覽器,我們可以搜到彩信相關(guān)的源代碼位于sf\app\messaging文件夾下,但是這個(gè)里面除了彩信之外,短信也有,郵箱也有,到底該如何找到需要的彩信呢,我們知道短信其實(shí)也有短信頭的,在SDK中有提供CSmsHeader
,懷疑彩信也應(yīng)該有這么頭才是,于是用C*Header搜索文件夾,過來能夠搜索到一個(gè)mmsheaders.h頭文件中有一個(gè)CMmsHeaders,找到了這個(gè)離勝利就不遠(yuǎn)了,聯(lián)想到短信讀取時(shí)有一個(gè)CSmsClientMtm::LoadMessageL()函數(shù),索性就找找有沒有一個(gè)CMmsClientMtm::LoadMessageL()函數(shù)的,通過sf\app\messaging\mmsengine\clientmtm\src文件加下的mmsclient.cpp果然能找到這個(gè)函數(shù),其源代碼如下
// ---------------------------------------------------------
// CMmsClientMtm::LoadMessageL
// Loads the multimedia message
// ---------------------------------------------------------
//
void CMmsClientMtm::LoadMessageL()
{
// First we should assert that iMsvEntry is not NULL, and panic, if it is
__ASSERT_DEBUG( iMsvEntry, gPanic( EMmsNoCMsvEntrySet ) );
// LoadMessageL should only be supported for message entries.
if ( iMsvEntry->Entry().iType.iUid != KUidMsvMessageEntryValue )
{
iAttributes->Reset();
iMmsHeaders->Reset();
iAddresseeList->Reset();
return;
}
// Old data must be reset first....
iAttributes->Reset();
// load the correct data
// get read-only message store
CMsvStore* store = iMsvEntry->ReadStoreL();
CleanupStack::PushL( store );
// restore headers of multimedia message
// Attachment info is not restored.
// It makes no sense to cache the attachment info as new attachments
// can be added with the help of the attachment magager without
// informing MMS Client MTM of the additions.
// Caller must use attachment manager to get attachment info.
iMmsHeaders->RestoreL( *store );
RestoreAttributesL( *store );
CleanupStack::PopAndDestroy( store );
store = NULL;
// Build the iAddresseeList up
BuildAddresseeListL();
}
注意源碼中紅色字體部分,我是英盲,所以也不管這個(gè)restore是啥意思,直接看CMmsHeaders里面的頭文件定義和源碼
頭文件定義
/**
* Internalize the headers.
* @param aStore CMsvStore
*/
IMPORT_C void RestoreL( CMsvStore& aStore );
其實(shí)看了頭文件里面的定義注釋,初始化頭,參數(shù)是
CMsvStore我們就知道是怎么回事了,不過既然有源碼,那就還是貼下源碼,至于分析我就不做了,反正跟短信讀取CSmsHeader一樣,操作這個(gè)CMmsHeaders。
// ---------------------------------------------------------
// CMmsHeaders::RestoreL
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsHeaders::RestoreL(
CMsvStore& aStore )
{
RMsvReadStream stream;
Reset( NULL ); // all old pointers are deleted here
if ( aStore.IsPresentL( KUidMmsHeaderStream ) )
{
stream.OpenLC( aStore, KUidMmsHeaderStream ); // pushes 'stream' to the stack
InternalizeL( stream );
CleanupStack::PopAndDestroy( &stream ); // close stream
}
// restore MMBox header streams if present
if ( aStore.IsPresentL( KUidMMsElementDescriptorStream ) )
{
stream.OpenLC( aStore, KUidMMsElementDescriptorStream ); // pushes 'stream' to the stack
iElementDescriptor = new( ELeave )CMmsElementDescriptor;
iElementDescriptor->InternalizeL( stream );
CleanupStack::PopAndDestroy( &stream ); // close stream
}
if ( aStore.IsPresentL( KUidMMsMMBoxMessageHeaderStream ) )
{
stream.OpenLC( aStore, KUidMMsMMBoxMessageHeaderStream ); // pushes 'stream' to the stack
iMmBoxMessageHeaders = CMmsMMBoxMessageHeaders::NewL();
iMmBoxMessageHeaders->InternalizeL( stream );
CleanupStack::PopAndDestroy( &stream ); // close stream
}
if ( aStore.IsPresentL( KUidMMsMMBoxViewHeadersStream ) )
{
stream.OpenLC( aStore, KUidMMsMMBoxViewHeadersStream ); // pushes 'stream' to the stack
iMmBoxViewHeaders = new( ELeave )CMmsMMBoxViewHeaders;
iMmBoxViewHeaders->InternalizeL( stream );
CleanupStack::PopAndDestroy( &stream ); // close stream
}
// Extended notification also restored here
TInt length = 0; // string length
// Completeness indicator is not saved or restored.
// If we have stored the text, it normally indicates that the message is not complete
if ( aStore.IsPresentL( KUidMMsExtendedNotificationStream ) )
{
stream.OpenLC( aStore, KUidMMsExtendedNotificationStream ); // pushes 'stream' to the stack
length = stream.ReadInt32L();
if ( length > 0 )
{
iExtendedNotificationText = HBufC::NewL( stream, length );
}
CleanupStack::PopAndDestroy( &stream ); // close stream
}
if ( aStore.IsPresentL( KUidMmsApplicationInfoStream ) )
{
stream.OpenLC( aStore, KUidMmsApplicationInfoStream ); // pushes 'stream' to the stack
length = stream.ReadInt32L();
if ( length > 0 )
{
iApplicationId = HBufC::NewL( stream, length );
}
length = stream.ReadInt32L();
if ( length > 0 )
{
iReplyToApplicationId = HBufC::NewL( stream, length );
}
length = stream.ReadInt32L();
if ( length > 0 )
{
iApplicationInfo = HBufC8::NewL( stream, length );
}
CleanupStack::PopAndDestroy( &stream ); // close stream
}
if ( aStore.IsPresentL( KUidMmsReserved ) )
{
stream.OpenLC( aStore, KUidMmsReserved ); // pushes 'stream' to the stack
iRecommendedRetrievalMode = stream.ReadInt32L();
length = stream.ReadInt32L();
if ( length > 0 )
{
iRecommendedRetrievalModeText = HBufC16::NewL( stream, length );
}
length = stream.ReadInt32L();
if ( length > 0 )
{
iReplaceCancelId = HBufC8::NewL( stream, length );
}
iCancelStatus = stream.ReadInt32L();
CleanupStack::PopAndDestroy( &stream ); // close stream
}
}
一來由于時(shí)間倉(cāng)促,二來開源的代碼頭文件和相關(guān)庫(kù)等要拷入工程中去,所以具體的編碼調(diào)試驗(yàn)證就留給以后正式要用的時(shí)候再進(jìn)行了。
彩信通知的內(nèi)容分析
以上對(duì)彩信的內(nèi)容分析算是告一個(gè)段落了,我們?cè)倩剡^頭來看看這個(gè)彩信通知,這個(gè)消息類型的數(shù)據(jù)項(xiàng)其mtm為KUidMsgMMSNotification
,那么這個(gè)客戶端的MTm到底是什么,繼續(xù)搜索源碼,發(fā)現(xiàn)在sf\app\messaging\mmsengine\clientmtm\src有一個(gè)mmsnotificationclient.cpp其內(nèi)部正好是CmmsNotificationClientMtm。本來以為其會(huì)跟彩信一樣比較容易看出結(jié)果來,誰知道這個(gè)類的源碼看了下,感覺有點(diǎn)殘缺的味道,里面也有用到彩信的頭CMmsHeaders,因?yàn)?span lang=EN-US>CMmsHeaders就沒有去深入,這下也只能打住了。而且還有一個(gè)疑問,如何通過這個(gè)彩信通知來編程實(shí)現(xiàn)手動(dòng)去獲取彩信,又是一個(gè)比較繁瑣的問題,搜了下源碼貌似跟sf\app\messaging\mobilemessaging\mmsui\notmtmsrc文件下的源碼有關(guān)??磥磉@趟渾水不太清啊,就此打住了。
暫時(shí)將Symbian端的彩信讀取分析到這里。以后有機(jī)會(huì)再繼續(xù)趟這渾水。
按照常規(guī),由于貼圖不會(huì)搞,如果需要詳盡文檔,請(qǐng)下載以下word文檔和諾基亞官方文檔rar的鏈接
彩信初探.rar
注意:
目前在6730機(jī)子上對(duì)草稿箱彩信進(jìn)行試驗(yàn)時(shí)發(fā)現(xiàn)
編輯的彩信假如沒有經(jīng)過發(fā)送,那么通過之前的attachment->AttachmentName()無法獲得附件的文件名;但是在N81等機(jī)子上是能夠獲取到的。雖然通過attachment->AttachmentName()無法獲取,但是目前發(fā)現(xiàn)通過attachment->FilePath()卻可以得到完整的文件路徑,為此附件文件名的獲取,假設(shè)attachment->AttachmentName()獲取不到,那么將采用attachment->FilePath()獲取文件路徑后進(jìn)行文件名提取的方法來實(shí)現(xiàn)比較可靠一些。
posted on 2010-07-28 21:16
frank.sunny 閱讀(2559)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
symbian 開發(fā)