近期要寫一個Android app。當中一個功能要發短信,直接照抄Android API Demos的樣例OS\SMS Messaging,在自己的手機上測試。發現總是報錯SmsManager.RESULT_ERROR_NO_SERVICE,理解不能。
於是開始Google。
發現網上非常少有人提到這個錯誤,並且Android上發短信,所有都是用的API Demos的發短信的樣例,或者使用Intent調用系統短信App來發短信。盡管用Intent調用系統短信App來發短信也能夠當作一個workround,但用戶體驗不好,感覺不爽。我的應用里的發短信流程應該是這種:用戶點擊button。彈出ProgressDialog。程序在后台悄悄的發短信。開槍的不要。然后告訴用戶短信發成功沒有。
既然網上差點兒所有的coder都用API Demos的短信樣例,包含幾本ebook(《Beginning Android Application Development - 8 Messaging and Networking》、《Professional Android 4 Application Development》)都是,我認為要么是Google在撒謊,Android的SmsManager事實上有重大BUG。要么就是我每次打開Ecllipse的方式不正確,或者我事實上生活在Matrix里。
NND。繼續深挖。於是就找到了“adb logcat -b radio”這個使用方法,即查看GSM模塊的通訊log。以下就是系統短信Activity和API Demos短信Activity的log比較:
系統短信Activity的log |
---|
06-10 15:18:26.058 D/SMS (28645): encoding detail>TextEncodingDetails { msgCount=1, codeUnitCount=2, codeUnitsRemaining=68, codeUnitSize=3, languageTable=0, languageShiftTable=0 } 06-10 15:18:26.178 D/RILJ_GSM( 418): [3085]> REPORT_SMS_MEMORY_STATUS: true 06-10 15:18:26.178 D/RILJ ( 418): [3086]> REPORT_SMS_MEMORY_STATUS: true 06-10 15:18:26.188 D/RIL_SWITCH( 100): CT_C+W_enable is NULL, set the value to disable. 06-10 15:18:26.188 D/RIL_SWITCH( 100): ril switch GO HTC RIL 06-10 15:18:26.188 D/RILJ ( 418): [3086]< REPORT_SMS_MEMORY_STATUS 06-10 15:18:26.208 D/RILJ_GSM( 418): [3085]< REPORT_SMS_MEMORY_STATUS 06-10 15:18:26.218 D/RILJ_GSM( 418): [3087]> REPORT_SMS_MEMORY_STATUS: true 06-10 15:18:26.218 D/RILJ ( 418): [3088]> REPORT_SMS_MEMORY_STATUS: true 06-10 15:18:26.228 D/RIL_SWITCH( 100): CT_C+W_enable is NULL, set the value to disable. 06-10 15:18:26.228 D/RIL_SWITCH( 100): ril switch GO HTC RIL 06-10 15:18:26.228 D/RILJ ( 418): [3088]< REPORT_SMS_MEMORY_STATUS 06-10 15:18:26.238 D/RILJ_GSM( 418): [3087]< REPORT_SMS_MEMORY_STATUS 06-10 15:18:26.679 D/GSM ( 418): laugnagetable/shifttable: 0/0 06-10 15:18:26.679 D/GSM ( 418): GEP countGsmSeptets: -1 06-10 15:18:26.679 D/SMS ( 418): sendRawPduWithBundle 06-10 15:18:26.689 D/SMS ( 418): checkInSegmentToRIL> SmsTracker@411f02c0, RetryCnt> 0 06-10 15:18:26.689 D/RILJ_GSM( 418): sendSMS pdu : 01000b813145189164f700080454755475 06-10 15:18:26.689 D/RILJ_GSM( 418): [3089]> SEND_SMS 06-10 15:18:27.410 D/RILJ_GSM( 418): [3090]> REPORT_SMS_MEMORY_STATUS: true 06-10 15:18:27.410 D/RILJ ( 418): [3091]> REPORT_SMS_MEMORY_STATUS: true 06-10 15:18:27.410 D/RIL_SWITCH( 100): CT_C+W_enable is NULL, set the value to disable. 06-10 15:18:27.410 D/RIL_SWITCH( 100): ril switch GO HTC RIL 06-10 15:18:27.410 D/RILJ ( 418): [3091]< REPORT_SMS_MEMORY_STATUS 06-10 15:18:29.812 D/RILMUX ( 744): main(2656) GSM0710 buffer. Stored 0 06-10 15:18:29.812 D/RILMUX ( 744): main(2657) Frames received/dropped: 8632/0 06-10 15:18:31.333 D/RILJ_GSM( 418): [3089]< SEND_SMS { messageRef = 232, errorCode = -1, ackPdu = null} 06-10 15:18:31.333 D/SMS ( 418): handleMessage > 2 06-10 15:18:31.333 D/SMS ( 418): pre error Code: -1 06-10 15:18:31.333 D/SMS ( 418): msgRef> 232, trytpmr> 0 06-10 15:18:31.333 D/SMS ( 418): send complete: SmsTracker@411f02c0 06-10 15:18:31.333 D/SMS ( 418): SMS send complete. Broadcasting intent: PendingIntent{411ce330: android.os.BinderProxy@40eb9c80} 06-10 15:18:31.333 D/SMS ( 418): framework sent intent: SMS_MO/number/1402384711344/1 06-10 15:18:31.433 D/RILJ_GSM( 418): [3090]< REPORT_SMS_MEMORY_STATUS 06-10 15:18:31.794 D/RILJ_GSM( 418): [UNSL]< UNSOL_RESPONSE_NEW_SMS 06-10 15:18:31.794 D/RILJ_GSM( 418): RIL_UNSOL_RESPONSE_NEW_SMS pdu : 0891683108200805F0040D91683145189164F70008416001518142230454755475 06-10 15:18:31.794 D/GSM ( 418): SMS SC address: +8613800280500 06-10 15:18:31.794 D/GSM ( 418): SMS SC timestamp: 1402384704000 06-10 15:18:31.804 V/RILC_IMC( 104): processWakeupCallback 06-10 15:18:31.804 D/SMS ( 418): handleMessage > 1 |
API Demos短信Activity的log |
06-10 14:20:05.949 D/SMS (32003): encoding detail>TextEncodingDetails { msgCount=1, codeUnitCount=13, codeUnitsRemaining=147, codeUnitSize=1, languageTable=0, languageShiftTable=0 } 06-10 14:20:05.959 D/GSM (32003): SMS status report requested 06-10 14:20:05.959 D/GSM (32003): laugnagetable/shifttable: 0/0 06-10 14:20:05.959 D/GSM (32003): GEP countGsmSeptets: 13 06-10 14:20:05.969 D/GSM (32003): charToLanguageTable/shifttable: android.util.SparseIntArray@4100bf78/android.util.SparseIntArray@41014aa8 06-10 14:20:05.969 D/GSM (32003): htc septets count/septets: 13/13 06-10 14:20:05.969 D/CDMA ( 418): [RuimSmsInterfaceManager] sendRawPdu: smsc=null pdu=[B@40f6a400 sentIntentPendingIntent{40f6a430: android.os.BinderProxy@40d86ca0} deliveryIntentPendingIntent{40f6a450: android.os.BinderProxy@40d86d00} 06-10 14:20:05.969 D/SMS ( 418): sendRawPduWithBundle 06-10 14:20:05.969 D/SMS ( 418): handleNotInService, message send fail ss : 1 |
請恕我眼拙,沒能從上面的log里看出究竟API Demos短信Activity究竟哪里出錯了。
於是繼續Google,發現了不少有意思的東西:
- SilentSMS:作者用reflection調用了IccSmsInterfaceManager來操作發送短信。盡管看起來非常酷,可App的安裝須要root權限,所以我沒有急着測試這個project。
- Android SMS/MMS/Google Voice Sending Library:作者override了非常多Android telephony相關的類,還是beta版本號。感覺為了發一個短信而已,用不着這么大的lib吧?
- text+:一款用WIFI來發短信的免費Android App。還有非常多類似的產品。事實上這類產品已經脫離里簡簡單單的短信功能了,整個一社交型應用了。國內類似的應用也非常多,如微信、QQ等。僅僅只是text+等還是支持將message以SMS發到沒有安裝text+的手機上。
抱怨這么多,事實上就是糾結於為什么網上都能用SmsManager這個簡單的API來發短信,而我這邊就是不行?!
原因到底何在?。!。
於是繼續郁悶地測試,刪除系統短信草稿箱里的草稿,看到菜單“設置->短信(SMS)”,於是手賤地點進去:
發送報告 為您發送的每條信息請求一個發送報告 |
服務中心(卡槽一) +8613800XXXXXX |
服務中心(卡槽二) |
管理 UIM 卡信息 管理 CDMA UIM 卡中存儲的信息 |
管理 SIM 卡信息 管理 GSM SIM 卡中存儲的信息 |
發送報告,唔,這個勾沒打,預計會收不到delivery回饋……卡槽一卡槽二,唔,我這個是雙卡雙待的手機。是有兩個卡槽的…………wait。我了個去的。不會吧,難道是由於我這個雙卡雙待的手機沒有插電信的卡而電信的卡又是主卡SmsManager就TMD直接連到主卡上然后報錯了吧?!
SmsManager。你能更brief點嗎?
立刻找同事的單卡手機跑了下API Demos,短信發送成功……
又找了還有一個同事的手機,雙卡雙待。副卡槽空的。主卡槽是電信的,插了電信卡。跑API Demos,短信發送成功……
心中那個神獸奔騰啊
Google了三天。看了一堆資料。原來是這個原因……
OK。如今問題明朗了,后面的流程就是找找怎么在雙卡雙待並且僅僅插了一張卡或菏澤插了兩張卡、三張卡的手機上用SmsManager發、短、信。
找了一圈。發現還是要用reflection發掘SmsManager的隱藏API。寫了個reflect的工具:
同一時候dump了Android TelephonyManager全部方法的返回值。發現一些實用的信息:
tm.getCallState()=CALL_STATE_IDLE tm.getDataActivity()=DATA_ACTIVITY_NONE tm.getDataState()=DATA_DISCONNECTED tm.getDeviceSoftwareVersion()=00 tm.getNeighboringCellInfo()=[] tm.getNetworkCountryIso()=cn tm.getNetworkOperator()=46000 tm.getNetworkOperatorName()=中國移動 tm.getNetworkType()=NETWORK_TYPE_GPRS tm.getPhoneType()=PHONE_TYPE_GSM tm.getSimCountryIso()=cn tm.getSimOperator()=46000 tm.getSimOperatorName()=CMCC tm.getSimState()=SIM_STATE_READY tm.getVoiceMailAlphaTag()=語音信箱 tm.getVoiceMailNumber()=null tm.hasIccCard()=true tm.isNetworkRoaming()=false
參考了《android 雙卡雙待 發送短信 》,用reflect出來的SmsManager的send方法還是發送失敗。
臨時不研究了,至少眼下單卡機上是能夠發送短信的,雙卡雙待機就用walkround吧:
- 用Android公開的SmsManager方法發送短信
- 假設上一步失敗。就用reflect出來的SmsManager方法發送短信
- 假設還是失敗,就用Intent啟動本地SMS應用發短信
To be continued