• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            franksunny的個人技術空間
            獲得人生中的成功需要的專注與堅持不懈多過天才與機會。 ——C.W. Wendte

            關于CompleteWithAppPath函數

            一直以來以為這個函數在S60平臺是萬能的,特別是之前用這個函數也是百試不爽,今天無意間寫了個小Demo發現在我的N81手機上,假如傳遞“Data\\rpm.xml”返回值則為-28BadName;假如只傳遞文件名,則其返回值雖然為0,但是路徑卻變成了“c:sys\bin\ rpm.xml”。

            一頭的霧水啊,干Symbian也快有3個多年頭了,從2版本到現在5版本,居然在這個函數上沒搞靈清,實在是汗顏一下,結果又搜了些資料,先一個還是支持以前觀點的,見后面的補全文件路徑操作;后又在諾基亞論壇找到一個wiki——檢測應用程序路徑,發現這個函數在3rd上有變故,問題算是找到了,為此想著好久沒有更新博客了,來更新下吧,呵呵。另外跟這里的朋友道個喜,我上個月喜得千金,終于升級做爸爸了。

            關于這個函數,勸大家以后還是少用、不用為妙,當然明白就理能滿足應用那就用吧,我寫這個只是提醒大家其并沒有真正搜索補全路徑的功效。

            補全文件路徑操作

             

            s60下能用CompleteWithAppPath(aFileName)這個函數來補全文件路徑(aFileName可以不包含任何路徑信息,但是文件名必須完成,而且如果包含路徑信息,則必須正確),如果在UIQ下就不能用該工具函數,只能編寫通用與Symbian平臺的代碼。以下是一個實際的案例問答。

            提問:

            第一,我想知道CompleteWithAppPath(aFileName);這個在s60下的具體含義是什么?

            第二,在UIQ下能用什么代替它。

              下面是我寫的一小段程序,請改下

              CBitmap* CBitmap::NewL(TFileName& aFileName, TInt aIndex )

              {

              CBitmap* bmp = new( ELeave )CBitmap;

              //CFbsBitmap tmpb;

              //load the correct bitmap..

              TInt ret;

              //CompleteWithAppPath(aFileName);

             

              // load the bitmap from the mbm file

              CFbsBitmap* tmpb = new (ELeave) CFbsBitmap();

              CleanupStack::PushL(tmpb);

              User::LeaveIfError(tmpb->Load(aFileName, aIndex));

              CleanupStack::Pop(); // bitmap

              bmp->iSize = tmpb->SizeInPixels ();

              bmp->iDrawRect = bmp->iSize;

              bmp->iData = new( ELeave ) TUint16[ bmp->iSize.iWidth * bmp->iSize.iHeight ];

              bmp->iMode = tmpb->DisplayMode();

             

              TUint16* dst = ( TUint16* )bmp->iData;

              for ( TInt y=0; y<bmp->iSize.iHeight; y++ )

              {

              TPtr8 buf( ( TUint8* )dst, bmp->iSize.iWidth*2 );

              tmpb->GetScanLine( buf, TPoint( 0, y ), bmp->iSize.iWidth, EColor4K );

              dst += bmp->iSize.iWidth;

              }

              tmpb->Reset();

              delete tmpb;

              tmpb = NULL;

              return bmp;

              }

            解決:

            1.CompleteWithAppPath(aFileName)根據當前程序的安裝位置補上aFileName中缺少的路徑組成部分:

            Code:

            Example1:

            TFilename fname = _L("\testdir\pics.mbm"); // Use _LIT instead

            CompletePathWithAppPath( fname );

            Result: fname == "c:\testdir\pics.mbm" if application was installed to c:

             

            Example2:

            TFilename fname = _L("pics.mbm"); // Use _LIT instead

            CompletePathWithAppPath( fname );

            Result: fname == "c:\system\apps\myapp\pics.mbm" if application was installed to c:

             

            2.UIQ上可以使用Symbian本身提供的TParse來做路徑的解析:

            Code:

            TFileName filename;

            TParse parse;

            parse.Set( CEikonEnv::Static()->EikAppUi()->Application()->AppFullName(), NULL, NULL);

            filename.Copy( parse.DriveAndPath() );

            //filename.Copy( parse.Drive() );

             

            檢測應用程序路徑

             

            詳細描述終端用戶將會選擇將程序安裝到C盤(手機存儲)或E盤(存儲卡或內置硬盤)中。程序有時需要知道自己的安裝位置,對程序安裝位置的判斷取決于S60平臺的版本。

            解決方案S60第二版

             

            S60第二版中,使用aknutils.h中的CompleteWithAppPath(TDes& aFileName)方法。這個方法將返回給定一個描述符,內有所有需要的組成部分(盤符,路徑,包括后綴的文件名)。任何缺少的部分(路徑和盤符)將從應用程序路徑(<drive>:\system\apps\<application_name> )中獲取

            #include <aknutils.h>    // Insert the full application path into the file name (fileName)  

            TFileName fullPath(fileName);  

            CompleteWithAppPath(fullPath);    // from aknutils.h

            解決方案S60第三版

            S60第三版向后,所有二進制程序(ExeDll文件)都存儲在\Sys\bin中。要訪問這個目錄,程序需要AllFiles能力。資源和數據是無法存入這個目錄的。程序有自己的目錄\private\<SID>\各自安放,這里SID是一個安全標識,每個程序都是獨一無二的。程序通過這個私有目錄存放.ini.mbm.rsc和數據文件。其他沒有AllFiles能力的程序將無法訪問它們。

            S60第三版上CompleteWithAppPath()方法將總是返回\sys\bin作為程序安裝目錄。如果要訪問程序的私有目錄中的數據,將要用另一個方法來獲得路徑:

            TFileName appPath;

            TBuf<2> appDrive;   // Returns private path of this application 

            // in following format: \Private\<SID of the application>\ 

            // (does not contain drive specification). 

            iEikonEnv->FsSession().PrivatePath( appPath );

            // Extract drive letter into appDrive 

            appDrive.Copy(iEikonEnv->EikAppUi()->Application()->AppFullName().Left(2));

            // Insert drive letter into path

            appPath.Insert(0, appDrive);

            posted @ 2010-07-09 19:42 frank.sunny 閱讀(1944) | 評論 (2)編輯 收藏
                 摘要: 本貼以后做為一些小問題的解決方案帖,遇到問題能夠通過網絡簡單解決的問題會不定期的來這里更新下,給自己和有同樣問題的人留個Mark  閱讀全文
            posted @ 2010-05-14 19:38 frank.sunny 閱讀(3748) | 評論 (6)編輯 收藏

             

            關于Symbian識別移動、聯通和其它運營商網絡的方法

             

            自從Symbian OSEKA2提供了強大的CTelephony,這之后很多跟網絡相關的參數都通過這個獲取,像識別目前手機是注冊的是移動還是聯通等信息網絡上傳的比較多的也是通過該方法,特別是嘯天兄在nokia論壇分享了代碼之后,網絡上大多采用其代碼,當然也包括我這樣的懶人在內,而且往往很多人都沒有注意最關鍵的原理即“國際移動用戶識別碼(IMSIInternational Mobile Subscriber Identification Number)是區別移動用戶的標志,儲存在SIM卡中,可用于區別移動用戶的有效信息。IMSIMCCMNCMSIN組成,其中MCC為移動國家號碼,由3位數字組成,唯一地識別移動客戶所屬的國家,我國為460MNC為網絡id,由2位數字組成,用于識別移動客戶所歸屬的移動網絡,中國移動為00,中國聯通為01MSIN為移動客戶識別碼,采用等長11位數字構成”。具體詳見http://wiki.forum.nokia.com/index.php/%E5%8C%BA%E5%88%86%E5%BD%93%E5%89%8D%E7%94%A8%E6%88%B7SIM%E5%8D%A1%E6%98%AF%E7%A7%BB%E5%8A%A8%E8%BF%98%E6%98%AF%E8%81%94%E9%80%9A

            最近在使用該代碼時,發現假如當手機處于離線狀態下,則不論有無插SIM卡,使用嘯天兄的方法,就識別不出來了,這個應該跟CTelephony的實現有關,本人試著通過其源碼去了解了,但是貌似跟蹤到底層沒有完全公開,或者說個人看源碼能力還太弱了些。由于采用嘯天兄方法實現不了了,所以只能從上面的紅頭文件(即紅體字)尋找解決方法,雖然CTelephony::GetCurrentNetworkInfo在離線模式下失效,但是CTelephony::GetSubscriberId仍然可用,為此我們就可以通過直接分析IMSI來實現對運營商網絡的識別,至于MNC的信息,大家可以查詢http://en.wikipedia.org/wiki/Mobile_network_code,在國內的情況如下截圖:

            既然知道了如上信息,我們就可以簡單的對IMSI號進行分析了,小可對嘯天兄的代碼進行簡單修改,當然該代碼也是使用CTelephony,只能使用在EKA2平臺上,EKA1可以采用RMobilePhone::GetSubscriberId的方法來獲取IMSI,在這里也就不做展開了,具體代碼如下:

            頭文件

            /*

             * TelephonyAO.h

             *

             *  Created on: 2010-4-22

             *      Author: frank

             */

             

            #ifndef TELEPHONYAO_H_

            #define TELEPHONYAO_H_

             

            #include <e32base.h>

            #include <Etel3rdParty.h>

             

            typedef enum

            {

                   ENetWorkUnKnow,

                   ENetWorkCM,

                   ENetWorkUN,

                   ENetWorkTC,

                   ENetWorkTT,

            }TNetWorkType;

             

            class CTelephonyAO : public CActive

            {

            public:

                   static CTelephonyAO* NewL();

                   TNetWorkType GetNetWorkId();

             

            public:

                   ~CTelephonyAO();

             

            protected:

                   void DoCancel();

                   void RunL();

             

            private:

                   CTelephonyAO();

                   void ConstructL();

             

                   void GetNetWorkInfo();

             

            private:

                   CActiveSchedulerWait*                iActiveSchedulerWait;

                   CTelephony*                        pTelephony_;

                   CTelephony::TSubscriberIdV1             iSubscribId;

                   CTelephony::TSubscriberIdV1Pckg     iSubscriberIdPckg;

            };

             

            #endif /* TELEPHONYAO_H_ */

             

            實現文件

            /*

             * TelephonyAO.cpp

             *

             *  Created on: 2010-4-22

             *      Author: frank

             */

             

            #include "TelephonyAO.h"

             

            CTelephonyAO::CTelephonyAO() : CActive(EPriorityStandard), iSubscriberIdPckg(iSubscribId)

            {

                  

            }

             

            CTelephonyAO::~CTelephonyAO()

            {

                   delete pTelephony_;

                   pTelephony_ = NULL;

                   delete iActiveSchedulerWait;

                   iActiveSchedulerWait = NULL;

            }

             

            void CTelephonyAO::ConstructL()

            {

                   pTelephony_ = CTelephony::NewL();

                   iActiveSchedulerWait = new (ELeave)CActiveSchedulerWait;

                   CActiveScheduler::Add(this);

            }

             

            CTelephonyAO* CTelephonyAO::NewL()

            {

                   CTelephonyAO* pSelf = new(ELeave) CTelephonyAO;

                   CleanupStack::PushL(pSelf);

                   pSelf->ConstructL();

                   CleanupStack::Pop();

                   return pSelf;

            }

             

            void CTelephonyAO::RunL()

            {

             

                   if (iActiveSchedulerWait->IsStarted())

                   {

                          iActiveSchedulerWait->AsyncStop();

                   }

            }

             

            void CTelephonyAO::DoCancel()

            {

                   pTelephony_->CancelAsync(CTelephony::EGetSubscriberIdCancel);

                   if (iActiveSchedulerWait->IsStarted())

                   {

                          iActiveSchedulerWait->AsyncStop();

                   }

            }

             

            void CTelephonyAO::GetNetWorkInfo()

            {

                   Cancel();

                   pTelephony_->GetSubscriberId(iStatus, iSubscriberIdPckg);

                   SetActive();

                   iActiveSchedulerWait->Start();

            }

             

            TNetWorkType CTelephonyAO::GetNetWorkId()

            {

                   GetNetWorkInfo();

                   TNetWorkType vNetWorkType = ENetWorkUnKnow;

                   if(iSubscribId.iSubscriberId.Length() < 15)

                   {

                          vNetWorkType = ENetWorkUnKnow;

                   }

                   else

                   {

                          TBuf<6> vHeader;

                          vHeader.Copy(iSubscribId.iSubscriberId.Left(5));

                          TBuf<3> vPtrTemp;

                          vPtrTemp.Copy(vHeader.Right(2));

                          TInt vNetWorkId = 0;

                          TLex vLex(vPtrTemp);

                          vLex.Val(vNetWorkId);

                          if((vNetWorkId == 1) ||(vNetWorkId == 6))

                          {

                                 vNetWorkType = ENetWorkUN;

                          }

                          else if((vNetWorkId == 0) ||(vNetWorkId == 2))

                          {

                                 vNetWorkType = ENetWorkCM;

                          }

                          else if((vNetWorkId == 3) ||(vNetWorkId == 5))

                          {

                                 vNetWorkType = ENetWorkTC;

                          }

                          else if(vNetWorkId == 20)

                          {

                                 vNetWorkType = ENetWorkTT;

                          }

                          else

                          {

                                 vNetWorkType = ENetWorkUnKnow;

                          }

                   }

                   return vNetWorkType;

            }

            至于如何調用,就可以通過如下簡單獲取了,不用再自己去比較了。

                   CTelephonyAO* pTelephony = CTelephonyAO::NewL();

                   CleanupStack::PushL(pTelephony);

                   TNetWorkType vNetWorkType = pTelephony->GetNetWorkId();

                   CleanupStack::PopAndDestroy(pTelephony);

            好了,暫時小結如下吧,感謝嘯天兄前人植樹。

            posted @ 2010-04-22 19:13 frank.sunny 閱讀(2187) | 評論 (3)編輯 收藏
                 摘要:   關于RMsgQueue類的使用   RMsgQueue類是Symbian OS EKA2才提供的一個類,最近因為項目中要使用,為此對使用進行如下小結。 因為RMsgQueue類只是一個封裝好的內核資源類,類似于RSocket和RTimer類,要想使用它進行異步操作就必須對其用AO類來封裝,從而來實現監聽消息,在有消息過來時得到通知并根據消息內容進行相對應的處理。 ...  閱讀全文
            posted @ 2010-04-09 21:40 frank.sunny 閱讀(2026) | 評論 (1)編輯 收藏

             

            關于3rd程序啟動是參數的小結

             

            Symbian 3rd手機上對于GUI程序的啟動方式可以采用安裝時自啟動、開機自啟動、編碼啟動和點擊功能表圖標的方式啟動,有很多軟件往往對采用何種方式啟動需要做些前后臺以及其它相關的處理方式,那么如何識別目前的GUI程序是何種啟動呢?

            方法就是通過重載GUI框架的C*AppUi:: ProcessCommandParametersL (CApaCommandLine &aCommandLine)函數(該函數還有其它重名函數,這里只對最簡單的形式進行解析,拋磚引玉),對其入參進行識別來判斷是何種形式的自啟動。下面對我試驗中的幾種情況給出示例,方便以后查詢

            1、安裝時啟動,即pkg中標記為FR,RI標記的啟動方式

            C*AppUi::ProcessCommandParametersL中入參的

            aCommandLine.OpaqueData().Length()0

             

            2、通過Startup List開機自啟動的方式

            結果參數與1

             

            3、通過點擊程序圖片啟動

            C*AppUi::ProcessCommandParametersL中入參的

            aCommandLine.OpaqueData().Length()1,具體符號,搞不出來,反正是ASCII碼值為1的那個符號

             

            4、代碼編程啟動

            試過三種方式:

            第一種

                   const TUid KAppUid={0x2CC2D30E};

                   TThreadId app_threadid;

                   RApaLsSession ls;

                   User::LeaveIfError(ls.Connect());

                   TInt err=ls.StartDocument(KNullDesC, KAppUid, app_threadid);

                   ls.Close();

            結果同3

             

            第二種

               RApaLsSession apaLsSession;

               User::LeaveIfError(apaLsSession.Connect());

               CleanupClosePushL(apaLsSession);

               TApaAppInfo appInfo;

               TInt retVal = apaLsSession.GetAppInfo(appInfo, aAppUid);

               if (retVal == KErrNone)

               {

                   CApaCommandLine* cmdLine = CApaCommandLine::NewLC();   

                   cmdLine->SetCommandL(EApaCommandRun);

                    cmdLine->SetExecutableNameL(appInfo.iFullName);

                   _LIT8(KExitDesC,"start");

                   cmdLine->SetOpaqueDataL(KExitDesC);

                   User::LeaveIfError(apaLsSession.StartApp(*cmdLine));

                   CleanupStack::PopAndDestroy(cmdLine);

               }

               else

               {

                   // The application not found!     

               }

               CleanupStack::PopAndDestroy(&apaLsSession);

            結果還是同3

             

            第三種

               _LIT8(KExitDesC,"start");

               CApaCommandLine *cmd=CApaCommandLine::NewLC();

               cmd->SetCommandL(EApaCommandRun);

               cmd->SetExecutableNameL(_L("start.exe"));

               cmd->SetOpaqueDataL(KExitDesC);

               RApaLsSession als;

               User::LeaveIfError(als.Connect());

               CleanupClosePushL(als);

               User::LeaveIfError(als.StartApp(*cmd));

               CleanupStack::PopAndDestroy(2);

            結果與上述就不一樣了,aCommandLine.OpaqueData()內容就是"start",也即五位描述符長度的內容。對于第二種和第三種情況,我是百思不得其解,為什么傳執行文件名就可以,而傳完整路徑就是不行。

            好了,暫時小結到這里。好久沒更新空間,今天就上來碼幾個字。

             

             

             

            posted @ 2010-03-26 17:10 frank.sunny 閱讀(1645) | 評論 (0)編輯 收藏

             

            聲音提示和震動提示的開發

             

            聲音提示可以采用兩種方法:一種是利用系統自帶的CoeSoundPlayer類來實現單音鈴聲的播放;另一種則是利用S60提供的多媒體框架CMda*類來實現音頻播放。

            關于聲音提示的使用

            CoeSoundPlayer類使用

            該類聲明于coesndpy.h頭文件,庫是cone.lib,最簡單的使用莫過于如下格式的代碼應用

            TBaSystemSoundType a(KSystemSoundMessageUID);

            CoeSoundPlayer::PlaySoundNow(a);

            在以上代碼的使用時,第一行聲明一個系統tone的類型,該類型聲明在bassnd.h文件中,同時在mmp中加上bafl.lib庫文件。通常這種簡單應用,在模擬器上能夠聽到聲音(3rd MR版本的模擬器上都聽不到聲音),但是在真機上,基本聽不到聲音,一個原因據說是默認的缺省音量被置成了KSystemSoundDefaultVolume,其值最大可以到100(我親測的結果是最小為0,沒有聲音,最大只能到10,超過10之后和傳負值一樣都會報MMFAudioClient 4的錯誤,程序也會Crash。所以關于這點最好還是有高人指點下)。另外bassnd.h中定義的類型還有KSystemSoundRingUID, KSystemSoundAlarmUID, KUidSystemSoundError, KUidSystemSoundEvent等,具體的效果,可以自己親測下。

             

            稍微復雜一點的應用代碼如下:

            TBaSystemSoundType soundType(KSystemSoundMessageUID);

            //TBaSystemSoundInfo::TTone soundTone(aFrequency, aDuration);

            TBaSystemSoundInfo::TTone soundTone(1500, 3*1000*1000);

            TBaSystemSoundInfo soundInfo(soundType, soundTone);

            BaSystemSound::SetSoundL(CCoeEnv::Static()->FsSession(), soundInfo);

            CoeSoundPlayer::PlaySoundNow(soundType);

            在這里,我對音調不是很懂,但是aFrequency的值,經人測試1003400是工作正常,效果很好(可能10003000最好), 36003800就變弱了,再往上到4000基本上已經聽不到了。這種方法一般在真機上還是可以感受出來的,并非像第一種情況,只有模擬器上有效果。

             

            只是長時間播放簡單的音調估計很刺耳,那么我們就可以通過事先設計好的rng文件來進行播放單音鈴聲,具體代碼如下:

            _LIT(KRingToneFileName1, "\\Data\\Sounds\\simple\\alarm.rng");

            const TInt KRingingTypeSilent = 4; // Silent

            TInt tRingingType (0);

            CRepository* tRepository = CRepository::NewLC(KCRUidProfileEngine);

            User::LeaveIfError( tRepository ->Get(KProEngActiveRingingType, tRingingType ) );

            if ( tRingingType != KRingingTypeSilent )

            {

                   TBaSystemSoundType soundType(KSystemSoundRingUID);

                   TBaSystemSoundName soundName(KRingToneFileName1);

                   CompleteWithAppPath(soundName);

                   TBaSystemSoundInfo soundInfo(soundType, soundName, 10,
                    KSystemSoundDefaultPriority);

                   BaSystemSound::SetSoundL(CCoeEnv::Static()->FsSession(), soundInfo);

                   CoeSoundPlayer::PlaySoundNow(soundType);

                   //CoeSoundPlayer::PlaySound(soundType);

            }

            CleanupStack::PopAndDestroy(); // tRepository

            使用以上代碼需要注意的是alarm.rng文件必須要有,否則會沒有聲音傳出,該文件在FP2版本的模擬器路徑下沒有,可以在S60_3rd_MR\Epoc32\release\winscw\udeb\z\system\sounds\simple下找到,并將其拷貝到相應的epoc32\release\winscw\udeb\z\system\sounds\simple下面即可。

            注:另外,在這里雖然對情景模式是否靜音進行了判斷,其實不判斷也是可以的,因為情景模式設為靜音,仍然是可以播放出聲音來的。這點很不同于震動。

             

            多媒體框架的使用

            S60MMF(多媒體框架)提供了對音頻進行播放、錄制和格式轉換等功能函數,具體的功能類如下:

            CMdaAudioPlayerUtility:音頻播放;

            CMdaAudioRecorderUtility:音頻錄制;

            CMdaAudioConvertUtility:音頻格式轉換;

            CMdaAudioToneUtility:音調播放

            CMdaAudioInputUtility/ CMdaAudioOutputUtility:音頻流操作

            對于這一塊內容的介紹在靈活使用EMCCsoft提供的AudioPlayer例子程序就會比較清楚,在這里就不多做展開了。唯一需要提醒的是,相應的回調接口虛函數一定要實現,否則不好控制。另設置音量的函數SetVolume也是只能在0~10之間,否則也會報MMFAudioClient 4錯誤。

             在用CMdaAudioPlayerUtility進行音樂文件比如*.wav格式播放時,假如一個文件還沒有播放完,又開始播放一個新文件,也會引發MMFAudioClient 4的錯誤。

            關于震動提示的使用

            震動這個接口的發展歷史很奇特,Symbian OS v8.x之前沒有提供震動接口,之后開始使用CVibraControl類提供震動接口,而在Symbian OS v9.x之后在保留原有接口基礎上又提供了新的CHWRMVibra類來提供震動接口。

            網上的代碼很多,常見形式如下:

            // for S60 2nd FP2 and FP3

            #include <vibractrl.h>  // CVibraControl, VibraCtrl.lib

             

            void DoVibrateL( TUint16 aDuration )

            {

              CVibraControl* control = VibraFactory::NewL();

              // get vibration setting in the user profile

              if ( CVibraControl::EVibraModeON == control->VibraSettings() ) 

              {

                 control->StartVibraL( aDuration );

              }

             

              delete control;

              control = NULL;

            }

             

            // for S60 3rd

            #include <hwrmvibra.h>  // CHWRMVibra, HWRMVibraClient.lib

             

            void DoVibrateL( TInt aDuration )

            {

              CHWRMVibra* vibra = CHWRMVibra::NewLC();

              // get vibration setting in the user profile

              if ( CHWRMVibra::EVibraModeON == vibra->VibraSettings() ) 

              {

                 vibra->StartVibraL( aDuration );

              }

             

              CleanupStack::PopAndDestroy( vibra );

            }

            事實上如果原封不動拷貝如上代碼是實現不了震動功能的,因為不管是CHWRMVibra還是CVibraControl對象在被新建并調用完StartVibraL函數之后,立即就被析構了,因為StartVibraL有類似異步函數的功能,并非阻塞在持續時間之內才會返回,所以對象還沒起振就刪除了。

            震動功能的實現代碼雖然簡單,但是要想震起來還是有點麻煩的,為此我在使用時除了以上問題,還遇到其它幾個問題:

            當前情景模式里震動提示設置為關時,顯然會因為

            if ( CVibraControl::EVibraModeON == control->VibraSettings() )

            if ( CHWRMVibra::EVibraModeON == vibra->VibraSettings() )

            兩個條件判斷沒通過而沒有真實調用StartVibraL函數,那么我如果將判斷去掉,始終讓其調用StartVibraL函數應該也會震動的吧?

            結果是震動函數返回-21KErrAccessDenied(拒絕接受),這和播放聲音提示時的效果完全兩樣,所以說讀情景配置模式里的參數在這里完全是必要的。

            好,那就加判斷,總算執行到了StartVibraL (TUint16 aDuration, TInt aIntensity)函數,假如在這里aDuration超過KHWRMVibraMaxDuration,或者aIntensity不在-100100之間(這里的強度值是馬達的運轉強度值,負值是馬達反轉,有些文章說該值在+-30范圍內會報-2KErrGeneral錯誤,但是自己用E65親測過,在+-30以內,沒有報錯,震動感不強烈而已,可能跟手機和具體硬件有關吧),那么震動效果又沒有起來,此時的震動函數返回為-6,即KErrAgument(錯誤要求)。

            我們解決了以上兩個問題后,還有兩種特殊情況,一種是當你的手機在充電時,如果調用正確的StartVibraL會返回-22錯誤,即KErrLocked(鎖閉)。以上幾種情況還好,雖然不震,但是你可以用TRAP機制捕獲錯誤碼,但是如果當你是通過數據線的手機PC模式裝上軟件后沒有拔出數據線,就算使用TRAP返回時KErrNone,但是手機還是沒有震動起來,你就會頭大了,難道這個函數在當前手機上不管用嗎?

            事實是,當你拔掉數據線,居然震動來了。

            唉,問題總算解決了,代碼雖簡單,但是實現卻并不簡單啊。

             

            posted @ 2009-08-12 17:38 frank.sunny 閱讀(1888) | 評論 (1)編輯 收藏

            RConnection 的基類是RSubSessionBase。目前我所知道的就是兩個功能:一、為套接字在SymbianOS上使用建立會話通道,二、使用已建立的會話通道

            第一種應用是為了屏蔽接入點(IAP)選擇對話框,具體代碼如下

            TInt currentProfileId;

             

            // Check whether we are offline or online

            iRepository->Get(KProEngActiveProfile, currentProfileId);

            if (currentProfileId == 5)

            {

                // Close and uninitialize

                iConnection.Close();

                iSocketServ.Close();

            }

            //這里的iSelectedIap是已經選擇好的接入點iap

            if(iSelectedIap == 0)

                   return;

             

            User::LeaveIfError(iSocketServ.Connect());

            User::LeaveIfError(iConnection.Open(iSocketServ));

            TCommDbConnPref pref;

            // IAP ID for connection to be used

            pref.SetIapId(iSelectedIap);

            //CEikonEnv::Static()->InfoWinL(_L("note:"), _L("Iap OK"));

            pref.SetDialogPreference( ECommDbDialogPrefDoNotPrompt);

            pref.SetDirection(ECommDbConnectionDirectionOutgoing );

            //CEikonEnv::Static()->InfoWinL(_L("note:"), _L("Pref Ok"));

            iConnection.Start(pref,iStatus);

            SetActive();

            由以上代碼可知,建立會話通道是一個異步函數,所以最好在活動對象中使用,因為目前該代碼只是在G網上親測過,在3GWLAN是否也通用暫時還不敢肯定。

             

            第二種應用往往用在復用通道上,比方說同一個程序有多個線程(或者使用C/S架構的程序,不同的進程乃至兩個完全無關的)需要使用網絡,沒必要每個線程都去建立一個會話通道,所以就采用復用的方式;還有一種復用的要求就是用RConnection來獲得當前鏈接的流量等數據,以下就給出這么一種應用的代碼:

            TUint      vUl = 0;

            TUint      vDl = 0;

            TPckg< TUint >  UplinkVolume(vUl);

            TPckg< TUint >  DownlinkVolume(vDl);

            TRequestStatus  aStatus;

            RConnection          vConnection;

            if(vConnection.Open(iSocketServ) == KErrNone)

            {

                   TPckgBuf<TConnectionInfo> connInfo;

                   TUint count;

                   if (vConnection.EnumerateConnections(count) == KErrNone)

                   {

                          for (TUint i=1; i<=count; i++)

                          {

                                 // Note: GetConnectionInfo expects 1-based index.

                                 if (vConnection.GetConnectionInfo(i, connInfo) == KErrNone)

                                 {    

                                        TInt vErr = vConnection.Attach(connInfo, RConnection::EAttachTypeNormal);

             

                                 }

                          }

                   }

            }

             

            vConnection.DataTransferredRequest(UplinkVolume, DownlinkVolume, aStatus);

            User::WaitForRequest(aStatus);

             

            TFileName vBuffergg,vBuffergg2;

            vBuffergg.AppendNum(vDl);

            iObserver.RComValuesL(EGetConnectId,_L("KDownlinkData"),vBuffergg,0); 

            vBuffergg2.AppendNum(vUl);

            iObserver.RComValuesL(EGetConnectId,_L("KUplinkData"),vBuffergg2,0);    

             

            vConnection.Close();

             

            代碼往往都是很簡單的,但是往往會有點問題,在這個獲取流量的方法上,起初我們使用單線程可以用,但是后來居然不能用了,百思不得其解,后來經過分析存在后面的一些問題,先得出一個結論,RConnection可以跨線程乃至進程使用

             

            注意點

            TConnectionInfoV2獲取的coninfo不能進行attach,否則會爆-34錯誤,如果同一個RConnection對象已經連接上了,再繼續使用attach,則會報-14錯誤,但是RConnection對象仍然是可以正常使用的,可以通過它創建Socket和監控流量等。

            在使用TPckg的時候尤其要注意類型的配對問題,因為上面代碼中用到的是局部變量,很顯然如下代碼不會有誤

            TUint      vUl = 0;

            TUint      vDl = 0;

            TPckg< TUint >  UplinkVolume(vUl);

            TPckg< TUint >  DownlinkVolume(vDl);

            但是一次當我將vUlvDl聲明成類的成員變量時,弄成如下方式

            TUint32   iUl = 0;

            TUint32   iDl = 0;

            再將其用TPckg進行封裝之后,傳入DataTransferredRequest函數,得到的流量始終是0,真的讓我百思不得其解,這種失誤教訓真的太深刻。


                另外,近來發現RConnection類的實例對象是不能夠進行跨線程調用,同樣的CRepository的實例對象跨線程調用也是不允許的。

            posted @ 2009-08-12 17:37 frank.sunny 閱讀(2488) | 評論 (0)編輯 收藏

             先聲明一下,下面的這段代碼是調用系統的瀏覽器實現網頁瀏覽的功能,很顯然這是一種比較簡單的方法,但是它的可控制性就不行了,例如左軟鍵的內容你是肯定該不了的。如何寫一個自己的瀏覽器,而不調用系統的,等以后做出來再說吧。

            調用系統的瀏覽器來實現網頁瀏覽可以根據系統瀏覽器的狀態而決定調用的方法,例如當系統瀏覽器正在使用的時候可以用TApaTask::SendMessage ()方法;當系統瀏覽器沒有被使用的時候可以用RapaLsSession::StartDocument() 方法。

            下面是實現代碼:

            TBool CinternetAppUi::ConnectL(const TDesC& addr)

            {

             const TInt KBrowserUid = 0x10008D39;

             TUid id( TUid::Uid( KBrowserUid ) );

             TApaTaskList taskList( CEikonEnv::Static()->WsSession() );

             TApaTask task = taskList.FindApp( id );

             // the system browser is in use

             if ( task.Exists() )

                {

                HBufC8* param8 = HBufC8::NewLC( addr.Length() );

                param8->Des().Append( addr );

                task.SendMessage( TUid::Uid( 0 ), *param8 ); // Uid is not used

                CleanupStack::PopAndDestroy();

                }

             // the system browser is not in use

            else

             {

              RApaLsSession   appArcSession;

              User::LeaveIfError(appArcSession.Connect());    // connect to AppArc server

              TThreadId id;

              appArcSession.StartDocument( addr, TUid::Uid( KBrowserUid ), id );

              appArcSession.Close();

             }

             return ETrue;

            }

            //其中入口參數addr的格式是4”+” <Space>“+”<Url>”,例如“4  www.google.com

             

            其中采用以上方法不僅僅可以用于開啟網頁,還可以用于啟動安裝sis/sisx,具體示例代碼如下:

            RApaLsSession installSession;

            TThreadId threadId;

            TUid uid;

            uid.iUid = 0x101F875A;

            installSession.Connect();

            installSession.StartDocument(aFileName, uid, threadId);

            installSession.Close();

            該代碼自己沒有親測過,但是從理論上說應該可行,而且有大牛說uid都不用傳進去。

            另外播放音樂文件,網上也說可以通過該方法來實現,Uid分別如下:

            0x102072c3 (from S60 3rd Edition onwards)

            0x6c5b9d2 (S60 2nd Edition)

            RapaLsSession::StartDocument()功能還是很強大的,在這里只做摘錄,以后有機會再親測,不過用其打開網頁的確可行。

             

             

             

             

             

            posted @ 2009-08-12 17:36 frank.sunny 閱讀(1068) | 評論 (0)編輯 收藏

            Nucleus PLUS介紹

             

            今天在公司看到VIACBP系列CDMA手機方案用到了Nucleus +OS,說實話還是第一次聽說,所以查了些資料,引用別人的總結做個百科小文,原文鏈接如下:

            http://www.upsdn.net/html/2005-01/250.html

             

            一、Nucleus PLUS嵌入式操作系統的簡單介紹

            Nucleus PLUS嵌入式操作系統是目前最受歡迎的操作系統之一,是美國源代碼操作系統商ATI公司為實時嵌入式應用而設計的一個搶先式多任務操作系統內核,其95%的代碼是用ANSIC寫成的,因此非常便于移植并能夠支持大多數類型的處理器

            從實現角度來看,Nucleus PLUS 是一組C函數庫,應用程序代碼與核心函數庫連接在一起,生成一個目標代碼,下載到目標板的RAM中或直接燒錄到目標板的ROM中執行。

            Nucleus Plus內核在典型的CISC體系結構上占據大約20k空間,而在典型的RISC體系結構上占據空間為40k左右,其內核數據結構占據1.5k字節的空間。Nucleus Plus以其實時響應、搶先、多任務以及源代碼開放特性獲得在通訊、國防、工業控制、航空/航天、鐵路、網絡、POS、自動化控制、智能家電等領域的廣泛應用。

            Nucleus PLUS 采用了軟件組件的方法。每個組件具有單一而明確的目的,通常由幾個C及匯編語言模塊構成,提供清晰的外部接口,對組件的引用就是通過這些接口完成的。除了少數一些特殊情況外,不允許從外部對組件內的全局進行訪問。由于采用了軟件組件的方法,Nucleus PLUS各個組件非常易于替換和復用Nucleus PLUS的組件包括任務控制、內存管理、任務間通信、任務的同步與互斥、中斷管理、定時器及I/O驅動等。

             

            二、Nucleus具有的優點:

            1、提供源代碼

            Nucleus PLUS提供注釋嚴格的C源級代碼給每一個用戶。這樣,用戶能夠深入地了解底層內核的運作方式,并可根據自己的特殊要求刪減或改動系統軟件,這對軟件的規范化管理及系統軟件的測試都有極大的幫助。另外,由于提供了RTOS的源級代碼,用戶不但可以進行 RTOS 的學習和研究,而且產品在量產時也不必支付 License,可以省去大量的費用。對于軍方來說,由于提供了源代碼,用戶完全可以控制內核而不必擔心操作系統中可能會存在異常任務導致系統崩潰。

            2、性價比高

            Nucleus PLUS由于采用了先進的微內核 ( Micro-kernel ) 技術,因而在優先級安排,任務調度,任務切換等各個方面都有相當大的優勢。另外,對C++語言的全面支持又使得Nucleus PLUS Kernel 成為名副其實的面向對象的實時操作系統內核。然而,其價格卻比較合理。所以,容易被廣大的研發單位接受。

            3、易學易用

            Nucleus PLUS 能夠結合 ParadigmSDS以及 ATI自己的多任務調試器組成功能強大的集成開發環境,配合相應的編譯器和動態聯結庫以及各類底層驅動軟件,用戶可以輕松地進行 RTOS 的開發和調試。另外,由于這些集成開發環境 ( IDE ) 為所有的開發工程師所熟悉,因而,容易學習和使用。

            4、功能模塊豐富

            Nucleus PLUS除提供功能強大的內核操作系統外,還提供種類豐富的功能模塊。例如用于通訊系統的局域和廣域網絡模塊,支持圖形應用的實時化Windows模塊,支持Internet網的WEB產品模塊,工控機實時BIOS模塊,圖形化用戶接口以及應用軟件性能分析模塊等。用戶可以根據自己的應用來選擇不同的應用模塊。

             

            Nucleus PLUSRTOS內核可支持如下類型的CPUx86,68xxx,68HCxx,NEC V25, ColdFire, 29K,i960, MIPS, SPARClite, TI DSP, ARM6/7, StrongARM, H8/300H, SH1/2/3, PowerPC, V8xx, Tricore, Mcore, Panasonic MN10200, Tricore, Mcore等。可以說NUCLEUS+支持CPU類型最豐富的實時多任務操作系統。針對各種嵌入式應用,Nucleus PLUS 提供相應的網絡協議(如TCP/IPSNMP等),以滿足用戶對通訊系統的開發要求。另外,可重入的文件系統、可重入的C函數庫以及圖形化界面等也給開發者提供了方便。針對不同的CPU類型,Nucleus 還提供編譯器、動態連接庫、多任務調試器等相應的工具來配置用戶的開發環境。

            值得提出的是ATI公司最近還發表了基于Microsoft Developers Studio的嵌入式集成開發環境-NUCLEUS EDE。從而率先將嵌入式開發工具與Microsoft的強大開發環境結合起來,提供給工程師們強大的開發手段。

             

            三、 源代碼帶來的優勢眾所周知,Nucleus實時多任務操作系統提供給用戶源代碼。

            這除商務上給用戶帶來巨大益處(免交Royalty)外,還在技術方面給用戶極大的方便,即無需編寫和調試BSP,從而達到易學易用的目的,加速產品上市。對RTOS有一定知識的工程師一定清楚,使用RTOS最大的障礙在于編寫和調試BSP。大家知道,在調試目標系統的軟件之前,必須將目標與主機連接起來并建立通訊。為此,我們可以編寫一段監控程序(Monitor)。然而,如果要調試基于RTOS內核的程序,主機上的調試器(Debugger)除要與目標建立通訊外,更重要的是必須識別RTOS的任務,這樣才能進行任務級調試(Task-aware Debugger)。因此,只有Monitor是不夠的。如果我們選用的RTOS不提供源代碼,那么,主機上的調試器(Debugger)就只有通過用戶編寫的BSP來了解Kernel在現有硬件平臺上對各個任務進行調度的情況。顯然,編寫BSP必須對CPU目標系統的硬件以及應用軟件等有全面而深入的了解。一般說來,對于一個有一定硬件開發經驗的工程師來說,編寫一個新的BSP要花的平均時間為兩個月左右。這對于一個新手來說可是比較困難的。對于編寫BSP的工程師來說,另外一個更大的挑戰就是如何調試BSP,即如何驗證所編寫的BSP是否正確。通常剛剛焊接安裝好的PCB板中,硬件或軟件的故障(Bugs)是比較多的。甚至更常見的是CPU部分都沒有運作正常。有時時序錯誤和總線錯誤都還存在。在您把寫好的BSP燒入EPROM(或FLASH)中試圖將目標與主機建立聯系時,您幾乎100%地會發現根本無法通訊,眼前一團漆黑,不知是軟件有錯誤還是硬件不運轉。BSP在正常運行嗎?不得而知。在焦急和摸索中您可能發現幾個月已悄然而過。對于早期的實時操作系統來說,BSP是必由之路。然而,新一代的RTOS-Nucleus PLUS則避免了BSP帶來的痛苦過程。因為Nucleus的調試是基于全新的動態連接庫(DLL)。用戶只須通過監控程序(Monitor)或者BDM調試口(或者JTAG調試口)建立目標系統(Target)與主機(Host)之間的通訊,并給主機上的Debugger初始化特性中加入Nucleus的動態連接庫(DLLs),這時,調試器就能夠自動地去識別運行在目標系統中的Nucleus內核和各個應用任務,從而完成任務級調試。上述的動態連接庫(DLLs)是由RTOS廠商和Debugger廠商合作完成的,用戶無須自行編寫。因此,Nucleus的用戶只需要將精力放在基于Nucleus的編程工作中。對于一個新手,往往經過一天到兩天的學習和培訓,就可以投入到應用程序編制工作中去,無需花大量的時間去研究CPU,特定的硬件等。

            另外,由于有了源程序,用戶在調試程序時可以清楚地通過STEP INTO命令,追蹤到RTOS的內層中去,觀看和學習Kernel對任務的管理和調度機制。對于有志研究RTOS深層技術的工程師來說極為方便。對于Motorola 68KPowerPC,用戶可以利用GreenHill公司的Mutil調試器或TRACE-ICD來完成Nucleus PLUS的調試;對于ARMStrongARM,用戶可以利用ARM公司的SDT251調試器或TRACE-ICD來完成Nucleus PLUS的調試;對于Intel x86實模式,用戶可以利用Paradigm公司的調試器及其DLL來進行任務級調試;對于Intel x86保護模式則可以利用SSI公司Softprobe調試器和SSI DLL來調試;對于i960SH3/4ARM6/7MIPS等芯片,則可以使用ATI公司自己的UDB調試器來進行任務的調試。結論:動態連接庫(DLL)是在RTOS工具中新出現的應用趨勢,通過這種方式,用戶可以免去BSP帶來的麻煩,靈活方便地進行開發和調試,大大加速開發進度。Nucleus實時操作系統提供源代碼,支持豐富的CPU種類,配合各類DLL動態連接庫,為使用和研究RTOS技術的工程帶來極大的利益。

             進行ARM系列的開發需要大量的設備投入,另外如果做比較大型的系統,還必須要操作系統,購買一個好的操作系統也是需要幾十萬元ARM應用層研發可以建立在CC++及其他的大多數開發語言上,這對于軟件公司來說是很方便的,只要準備好硬件及操作系統,其他的工作就可以分模塊給N個人來進行。而對于底層的東西,ARM公司也可以有比較大力度的支持,因此如果需要用ARM系列開發高端產品,可行性是比較高的,開發周期也不會很長。當然,對于arm芯片,還是有一定的限制,比如沒有除法指令,這樣在編程時就要盡量避免用除法,否則會帶來程序代碼的增加和執行速度的降低。一般說,除法還是可以通過移位和乘法來代替。


            另外,好久沒上來碼字了,上周結束失業,估計金融危機對我的影響暫時結束一下了吧。

            posted @ 2008-11-27 22:37 frank.sunny 閱讀(1600) | 評論 (0)編輯 收藏

            這次失業之后,突然發現現在工作好像真的不是很好找,沒辦法,主動權不在自己手里,靜下心來想想就當通過筆試來給自己查漏補缺吧,昨天筆試遇到一個虛擬繼承的概念,這不雖說2分的題,但是這個玩意有大內容,我學習了下,也就先整個入門出來吧:

             

            為什么要引入虛擬繼承?

            虛擬繼承在一般的應用中很少用到,所以也往往被忽視,這也主要是因為在C++中,多重繼承是不推薦的,也并不常用,而一旦離開了多重繼承,虛擬繼承就完全失去了存在的必要(因為這樣只會降低效率和占用更多的空間,關于這一點,我自己還沒有太多深刻的理解,有興趣的可以看網絡上白楊的作品RTTI、虛函數和虛類的開銷分析及使用指導,說實話我目前還沒看得很明白,高人可以指點下我)。

            以下面的一個例子為例:

            #include <iostream.h>

            #include <memory.h>

            class CA

            {

                int k; //如果基類沒有數據成員,則在這里多重繼承編譯不會出現二義性

            public:

                void f() {cout << "CA::f" << endl;}

            };

             

            class CB : public CA

            {

            };

             

            class CC : public CA

            {

            };

             

            class CD : public CB, public CC

            {

            };

             

            void main()

            {

                CD d;

                d.f();

            }

            當編譯上述代碼時,我們會收到如下的錯誤提示:

            error C2385: 'CD::f' is ambiguous

            即編譯器無法確定你在d.f()中要調用的函數f到底是哪一個。這里可能會讓人覺得有些奇怪,命名只定義了一個CA::f,既然大家都派生自CA,那自然就是調用的CA::f,為什么還無法確定呢?

            這是因為編譯器在進行編譯的時候,需要確定子類的函數定義,如CA::f是確定的,那么在編譯CBCC時還需要在編譯器的語法樹中生成CB::fCC::f等標識,那么,在編譯CD的時候,由于CBCC都有一個函數f,此時,編譯器將試圖生成這兩個CD::f標識,顯然這時就要報錯了。(當我們不使用CD::f的時候,以上標識都不會生成,所以,如果去掉d.f()一句,程序將順利通過編譯

             

            要解決這個問題,有兩個方法:

            1、重載函數f():此時由于我們明確定義了CD::f,編譯器檢查到CD::f()調用時就無需再像上面一樣去逐級生成CD::f標識了;

            此時CD的元素結構如下:

            |CB(CA)|

            |CC(CA)|

            故此時的sizeof(CD) = 8;CBCC各有一個元素k

            2、使用虛擬繼承:虛擬繼承又稱作共享繼承,這種共享其實也是編譯期間實現的,當使用虛擬繼承時,上面的程序將變成下面的形式:

            #include <iostream.h>

            #include <memory.h>

            class CA

            {

                int k;

            public:

                void f() {cout << "CA::f" << endl;}

            };

             

            class CB : virtual public CA  //也有一種寫法是class CB : public virtual CA

            {                       //實際上這兩種方法都可以

            };

             

            class CC : virtual public CA

            {

            };

             

            class CD : public CB, public CC

            {

            };

             

            void main()

            {

                CD d;

                d.f();

            }

            此時,當編譯器確定d.f()調用的具體含義時,將生成如下的CD結構:

            |CB|

            |CC|

            |CA|

            同時,在CBCC中都分別包含了一個指向CA的虛基類指針列表vbptrvirtual base table pointer),其中記錄的是從CBCC的元素到CA的元素之間的偏移量。此時,不會生成各子類的函數f標識,除非子類重載了該函數,從而達到“共享”的目的(這里的具體內存布局,可以參看鉆石型繼承內存布局,在白楊的那篇文章中也有)。

            也正因此,此時的sizeof(CD) = 12(兩個vbptr + sizoef(int);

             

            另注:

            如果CBCC中各定義一個int型變量,則sizeof(CD)就變成20(兩個vbptr + 3sizoef(int)

            如果CA中添加一個virtual void f1(){}sizeof(CD) = 16(兩個vbptr + sizoef(int)+vptr;

            再添加virtual void f2(){}sizeof(CD) = 16不變。原因如下所示:帶有虛函數的類,其內存布局上包含一個指向虛函數列表的指針(vptr),這跟有幾個虛函數無關。

            以上內容涉及到類對象內存布局問題,本人還難以做過多展開,先貼這么多,本篇文章只是考慮對于虛擬繼承進行入門,至于效率、應用等未作展開。本文在網上文章基礎上修改了下而得此篇,原文載于http://blog.csdn.net/billdavid/archive/2004/06/23/24317.aspx

            另外關于虛繼承和虛基類的討論,博客園有篇文章《虛繼承與虛基類的本質》,總結得更為詳細一點。

             

             

            posted @ 2008-10-16 16:55 frank.sunny 閱讀(13760) | 評論 (8)編輯 收藏
            僅列出標題
            共7頁: 1 2 3 4 5 6 7 

            常用鏈接

            留言簿(13)

            隨筆分類

            個人其它博客

            基礎知識鏈接

            最新評論

            閱讀排行榜

            評論排行榜

            久久精品国产亚洲AV无码娇色| 精品久久亚洲中文无码| 无码人妻少妇久久中文字幕| 麻豆精品久久精品色综合| 91精品国产综合久久久久久| 国产精品美女久久久久网| 欧美精品乱码99久久蜜桃| 无夜精品久久久久久| 国产综合成人久久大片91| 日日狠狠久久偷偷色综合免费| 伊人色综合久久| 久久久久亚洲AV无码专区首JN| 久久精品夜色噜噜亚洲A∨| 久久久久久久综合狠狠综合| 狠狠色综合网站久久久久久久高清| 亚洲精品无码久久一线| 伊人久久大香线焦综合四虎| 成人资源影音先锋久久资源网| 精品久久久久久成人AV| 欧美精品一区二区久久| 国产一区二区三区久久| 亚洲综合久久夜AV | 国产精品一久久香蕉产线看| 无码人妻久久一区二区三区免费丨| 精品综合久久久久久97| 亚洲精品无码久久久久sm| 日产精品久久久久久久性色| 久久久久97国产精华液好用吗| 久久精品国产精品亚洲艾草网美妙 | 嫩草影院久久99| 久久综合狠狠综合久久激情 | 久久99精品国产麻豆蜜芽| 久久人人超碰精品CAOPOREN| 国内精品久久久久影院薰衣草| 久久亚洲国产成人精品性色| 亚洲午夜久久久精品影院| 久久久久久久久久免免费精品| 一本久久综合亚洲鲁鲁五月天| 性高湖久久久久久久久| 日本精品久久久中文字幕| 亚洲午夜福利精品久久|