Android 5.0 Default SMS App以及運營商授權SMS App


已同步更新至個人bloghttp://dxjia.cn/2015/08/android-5-default-sms-app/

 

 

題外話:博友們有沒有好用的寫博客客戶端推薦啊,cnblogs推薦的windows live writer和word都試過,都不是很好用啊,本地看着還可以,但發布出來排版就不是那么好看了。

 

正題:

  Android中短信的接收是這樣的一個過程:

  底層先將短信報給FW,FW處理過后,會將短信通過intent廣播的形式廣播出來,而注冊了接收短信廣播的APP們,就能收到並處理短信

Default SMS App

  而android在4.2開始,對操作SMS的app進行了限制,增加了default sms app的概念,只有default app才可以操作短信,而且default sms app可以由用戶來指定。

  先來看看整個系統初始化時,是如何來初始化default sms app的:

  依然在FW的phone框架初始化里,PhoneFactory. makeDefaultPhone

首先調用一次SmsApplication.getDefaultSmsApplication方法,並且指定第二個參數updateIfNeeded為true,就是如果沒有設置過就自動指定一個。

關於getApplication函數我在 《Android 5.0 雙模phone初始化分析》 一文中有講到,其指定default SMS App的規則:

  • 首先嘗試從用戶指定的默認app,對應的系統setting key為:sms_default_application;
  • 其次看是否有Google的官方 默認sms app;
  • 如果以上兩個都沒有,那么就從PM中獲取所有注冊有完整sms有關的broadcast receiver的app,從中找一個優先級最高的,並將其設定為default app。

  default sms app的值保存在setting db中, Settings.Secure.SMS_DEFAULT_APPLICATION

當然,SmsApplication也提供了set方法來讓用戶可以手動設置他想使用的default sms app

 

  另外,在PhoneFactory初始化里我們還看到在調用一次getDefaultSmsApplication后,還調用了另外一個方法:

  這個方法會監聽應用程序的安裝與卸載,並在有應用被安裝或者移除的時候,能夠及時自動更新default sms app,已保證default sms app是隨時都有設定的。

 

運營商授權SMS App

  后來的版本,android又增加了運營商授權SMS App的實現,原則是如果所有的sms app里,如果有一個是運營商授權指定的短信處理app,那么它就會有第一優先級,不管default app設定的是誰,都會只使用這個授權app來收發和管理顯示短信。

  那么這個運營商授權APP是在哪里指定的呢?答案是:是固化在icc卡里的,也就是運營商給你的手機卡(no-uim和no-sim的手機目前是處理不了的),卡在出廠的時候,會在卡里的某個固定單元文件寫上授權app的package name以及其簽名hash校驗值,在卡初始化完成后讀取這些值解析后保存,如果手機里有這個package name的app,並且簽名hash也一致,那么就說明該App是運營商授權sms app。

  完成這些信息初始化的類為 UiccCarrierPrivilegeRules,其內部完成對卡上文件進行讀取和解析,保存信息,並提供對外接口。

因為跟卡直接相關,所以UiccCarrierPrivilegeRules在UiccCard被創建后初始化。

  在UiccCard.update()函數里創建:

UiccCarrierPrivilegeRules

  先看看該類的class注釋,

  在構造函數中開啟讀取文件的流程,事件驅動。

 

  注釋里對該類的功能進行了講解,而且給出了使用到的icc card文件讀取和解析的spec規范文檔 GPD_SE_Access_Control_v1.0.20.pdf,可惜他給的鏈接無效了,可以在百度文庫上找到該spec,地址: GPD_SE_Access_Control_v1.0.20.pdf

  具體讀取icc文件和解析這里就不分析了,都是依照spec的實現。只說明下幾個接口和內部變量:

AccessRule

內部類,用來保存解析到的rules,內部維護單個rule的package name和簽名hash值。

List<AccessRule> mAccessRules;

保存所有的rules在list,看來可以支持多個運營商指定app

areCarrierPriviligeRulesLoaded()

是否已經准備好

getCarrierPrivilegeStatus()

驗證指定的package name的app是否有運營商授權

getCarrierPrivilegeStatusForCurrentTransaction()

驗證當前進程里是否存在有運營商授權的app(多個app可以通過共享id的形式運行在同一個進程里)

getCarrierPackageNamesForIntent()

通過從package manager中取出所有符合傳入的Intent的app,也就是取出所有可以處理傳入的Intent的app,並檢查這些app里是否有符合運營商授權的,並返回符合的list

具體使用示例

  以一條新短信的接收為例:

  在InboundSmsHandler里的processMessagePart()函數中,processMessagePart()函數用來將緩存的短信分段進行組裝,如果已經收全,就會將短信廣播出去,當然,如果是單段的獨立短信該函數也就直接廣播了,來看打包Intent廣播的部分:

  注意上面代碼中黃色高亮的部分,首先是新建一個intent,而這個intent的action直接指定為Intents.SMS_FILTER_ACTION?這個是什么鬼,以前沒見過啊。。。跳轉過去:

  注釋已經很清晰明了了,這個action只會發送給carrier app,而且carrier app可以通過set result為RESULT_CANCELED來終止這個廣播,這樣別的app就永遠沒有機會收到這個廣播了。

  回到之前的打包intent的代碼,其會去UiccCard里通過 getCarrierPackageNamesForIntent()方法來得到可以處理SMS_FILTER_ACTION的符合運營商授權的app name list,如果能取到,那么就將intent的目標package直接設定為那個app,這樣這個短信廣播就只會發送給這個授權app;

intent.setPackage(carrierPackages.get(0));

  而如果沒有運營商授權app,那么就會調用setAndDirectIntent (intent, destPort);來設定廣播app,這里才輪到default sms app:

  短信的desPort都是-1,所以可以只看上面這個if分支,首先先將intent的action修改為 Intents.SMS_DELIVER_ACTION, 這個是android的新短信常規intent action,頂替掉之前的SMS_FILTER_ACTION;然后通過getDefaultSmsApplication獲取到default sms app,如果能取到,那么通過intent.setComponent(componentName)設置目標package為這個app,如果沒有,那么就setComponent(null),這樣就可以廣播給所有可以接收SMS_DELIVER_ACTION的app。

 

另外,提一點另外的細節,打包廣播短信的地方:

  dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,

  AppOpsManager.OP_RECEIVE_SMS, resultReceiver, UserHandle.OWNER);

  第4個參數,傳入的resultReceiver,是個內部類SmsBroadcastReceiver對象,用來處理短信廣播的結果,對每種intent action廣播出去之后的處理結果都有分別處理,如從緩存數據庫總刪除短信、更新短信內容等。也用來如果發出去的廣播沒人處理,則使用最低級的SMS_RECEIVED_ACTION重新將廣播廣播出去等機制。。

 

總結

  FW初始化時,首先嘗試設定一個default sms app,同時,在卡槽的icc卡准備好后,開始讀取卡上的運營商授權app數據,並保存下來;新短信接收時首先通過接口獲取到運營商授權app,如果沒有,再通過接口獲取到default sms app,如果還沒有,就直接廣播啦。

  運營商授權app的優先級大於default sms app。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM