第1章 PhoneSwitcher的处理流程
图1 PhoneSwitcher类,初始时注册了广播接收器
同时,TelephonyNetworkFactory初始时,注册成PhoneSwitcher的观察者,监听Phone数据能力的切换。
图2注册成PhoneSwitcher的观察者
2、当PhoneSwitcher收到数据卡变化的广播后,其handleMessage调用onEvaluate函数。
图3调用handleMessage进行消息处理,调用onEvaluate
onEvaluate函数判断出数据卡确实发生变化后,就会激活数据卡对应的Phone的拨号能力;
同时去激活非数据卡对应phone的拨号能力。
图4激活数据卡对应的phone拨号能力
PhoneSwitcher中定义了PhoneState类,专门用于处理激活和去激活Phone拨号能力的工作,
同时维护对应Phone的激活状态。
图5 PhoneState类处理和激活Phone拨号能力
在完成上述工作后,PhoneSwitcher就会通知TelephonyNetworkFactory发生了Active Phone Switch。
图6 PhoneSwitcher通知TelephonyNetworkFactory
3、TelephonyNetworkFactory收到通知后,就会调用onActivePhoneSwitch进行处理。
在onActivePhoneSwitch函数中,首先会调用PhoneSwitcher提供的isPhoneActive接口,
查询并更新TelephonyNetworkFactory的激活状态。
图7 调用PhoneSwitcher提供的isPhoneActive接口
此时,对于处于激活状态的TelephonyNetworkFactory来说,就可进入后续的拨号的流程;
对于非激活的TelephonyNetworkFactory而言,则会进入后续断开数据连接的流程。
图8
PhoneSwitcher的这部分流程实际上起到了一种承前启后的作用。
终端加载完APN、设置完数据卡后,必须在这一步激活数据卡对应Phone的拨号能力,
后续的数据拨号流程才能顺利完成。
4、PhoneSwitcher的 InformDdsRil()方法
4.1)在ConnectivityService构造的时候就创建了一个默认的defaultRequest,mDefauleRequest。网络工厂连接上以后,就请求网络连接。
4.2)在ConnectivityService的handleAsyncChannelHalfConnect里面,如果已经连接到网络工厂,就把网络请求CMD_REQUEST_NETWORK发到对应的网络工厂。
接着就是网络工厂处理请求CMD_REQUEST_NETWORK。
4.3)NetWorkFactory的handleAddRequest进行处理,调用evalRequest来判断是开启网络还是释放网络:
如果开启网络则调用needNetworkFor,如果是释放网络则调用releaseNetWorkFor;
现在先关注移动网络开启数据的,由于移动网络的网络工厂是TelephonyNetworkFactory,在dctController里面进行定义的。
4.4)TelepjonyNetworkFactory:needNetworkFor
检查sub id是否符合,还有apn是否支持,如果符合条件,则调用requestNetWork
4.5)DctController:request
把请求加入到队列mRequestInfo中,紧接着调用processRequests来处理队列中的请求。
4.6)QtiDctController:onProcessRequest
根据请求判断是否需要切DDs,需要的话调用handleDdsSwitch
4.7)qtiDctController:handleDdsSwitch
根据情况是要关闭当前的数据,还是开始建立连接,关闭调用doDisconnectAll,建立连接调用doConnect
4.8)qtiDctController:doConnect
调用informDdsToRil通知底层默认的dds,然后通过DcSwitchAsyncchannel的connect发送请求Request_Connect
状态条转如下:IdleState->AttachingState->AttachedState->DetachingState->IdleState
在AttachingState会调用setDataAllowed为true,DetachingState会setDataAllowed为falsesetDataAllowed会把某个phone的ps变为attached,从而触发建立data call。
扩展:在插卡之前也会创建PhoneSwitcher,未插卡的时候PhoneId都为0;插卡之后把subid赋值给PhoneId。
第2章 SIMStateReceiver的处理流程
一)整体流程总结
三)具体代码分析
以下是对此的详细代码分析:
图2.1
插卡后,收到卡的状态变化的消息,或者收到phone启动后发送的广播消息ACTION_ BOOT_COMPLETED。
这里拿收到ACTION_ BOOT_COMPLETED来举例说明,收到该消息之后会执行sendPhoneBoot函数;在sendPhoneBoot函数中,会启动SimContactsService。
图2.2启动SimContentsService
第3章 PIN码设置、加密、存储
1.前提介绍:该密码保存在/data/misc/keystore/user_0/.masterkey,可在adb shell后,ls –al /data/misc/keystore/user_0/.masterkey查看文件存在与否。
下面讲述的是,从设置点击 安全-屏幕锁定方式,设定pin-设置pin码,通知-完成,到pin如何进行加密和存储的。完成这个button是一个设置中通用的一个按键,不再通知界面的布局中。
2. 这个按键响应ChooseLockPassword.java的onClick方法,执行以下代码:
图3.1执行onclick()
进入handleNext()方法,当确认密码正确时,执行startSaveFinsh操作
图3.2执行startSaveAndFinsh()
看一下startSaveFinsh()函数的具体执行过程,在该过程中会调用SaveAndFinishWorker类的start方法。
图3.3在该过程中会调用ChooseLockPassword.java文件中的SaveAndFinishWorker的start()方法
3. 进入ChooseLockPassword.java文件中的SaveAndFinishWorker类里面执行start()方法,看一下start方法的详细内容。
SaveAndFinishWorker继承于SaveChosenLockWorkerBase类,在SaveAndFinishWorker的satrt方法的最后会执行staart(),这个start是SaveChosenLockWorkerBase类里面的方法。
图3.4 ChooseLockPassword.java文件的SaveAndFinishWorker类里执行start()
图3.5在该start方法中会结束ChooseLockPassword 的saveAndVerifyInBackground()
图3.6调用LockPatternUtils.java文件的saveLockPassword()方法
saveLockPassword中会做一次密码检查是否有空或者是否是4位,pin只能是4位,再传pin password,类型,userhandle下去。
图3.7通过getLockSettings()间接调用LockSettingsService的setLockCredential方法
a. LockPatternUtils.java文件的getLockSettings()方法。
(在LockSettingsService类的onstart方法中有把mLockSettingService赋值给lock_settings,然后在LockPatternUtils中对其进行调用)。
实际上是:通过AIDL,调用的是 LockSettingsService类的service端的setLockCredential()方法。
图3.8getLockSettings调用的是 LockSettingsService类的lock_settings
b. 通过AIDL,调用的是 LockSettingsService类service端的setLockCredential方法。LockSettingsService类的onstart方法中有把mLockSettingService赋值给lock_settings,然后在第8步被调用了。
图3.9 LockSettingsService类中的start方法
在第7步中,LockPatternUtils.java文件的setLockCredential()方法中有如下代码:getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword, requestedQuality, userHandle)。
注意:验证都是经过LockSettingsService,然后再到GateKeeper。
所以:通过AIDL,调用的是 LockSettingsService.java service端的setLockCredential方法。
setLockCredential方法会调用setLockCredentialInternal()方法。
图3.10调用setLockCredentialInternal()方法
图3.11把pin存储起来
第4章 SIM卡锁定
1、IccLockSettings:
在UI界面,会触发onResume()函数。在onResume里面注册了ACTION_SIM_STATE_CHANGED这个事件,当收到这种广播的时候就会进行响应。
OFF_MODE状态代表关闭状态,也是无操作状态;
ICC_LOCK_MODE:进行pin码设定与解锁的状态;
ICC_OLD_MODE:更改pin码,输入旧pin码的状态;
ICC_NEW_MODE:更改pin码,输入新pin码的状态;
ICC_REENTER_MODE:更改pin码,再次输入pin码的状态。
图4.1获取卡状态,执行resetDialogState()
当我们点击要修改PIN时,mDialogState就会设置为ICC_OLD_MODE,接着就会执行setDialogValues()函数。
图4.2执行setDialogValues()
图4.3重置Dialog状态,最后执行setDialogMessage()
图4.4 setDialogMessage方法
总结:也就是说,当我们什么都不做时,默认的mDialogState==ICC_OLD_MODE状态。
2、pin码设定与解锁回调处理逻辑:
IccLockSettings.java中:
在第1步中的a.中onReceiver()会接收到TelephonyIntents发来的广播,接着就会发送MSG_SIM_STATE_CHABGED消息给自己,进行下一步处理。
图4.5MSG_SIM_STATE_CHABGED消息处理
图4.6 handleMessage处理消息
在updatePreferences()函数中获取当前PIN码的状态:
图4.7获取当前PIN码的状态
success 为true的条件为:ar.exception == null。
图4.8 MSG_ENABLE_ICC_PIN_COMPLETE消息处理
图4.9 MSG_CHANGE_IC_PIN_COMPLETE消息处理
当我们点击修改或者提交按钮的时候,会触发点击事件 onPinEnter()。它会根据不同的case,然后调用不同的函数,在调的函数中会进行pin状态变化的设置。
当我们进行把手机pin开启或关闭,会触发onPreferenceTreeClick(),它会把mDialogstate的状态设置为ICC_LOCK_MODE,之后我们才可执行修改pin的操作,这个时候mDialogState==ICC_OLD_MODE。
图4.10
初始的mDialogState==ICC_OLD_MODE,
进入handleMessage进行消息处理,case ICC_OLD_MODE:mDialogState被赋值为 ICC_NEW_MODE,这个是输入旧密码;
然后就会进入handleMessage处理,case:ICC_NEW_MODE,mDialogState被赋值为 ICC_REENTER_MODE,这个是输入新密码;
然后再去handleMessage处理,case:ICC_REENTER_MODE,这个是确认新密码;
这个流程就像是我们修改密码的时候会让你输入 原始密码---》新密码---》确认新密码的流程。
如果正确,就去else中执行tryChangePin()执行修改pin操作。否则会再次执行循环,直至达到最大次数。
图4.11 pin状态更迭
图4.12 tryChangeIccLockState函数改变pin状态
总结:为了防止主线程卡死,都用异步的消息传递方法,也就是请求接口时,需要构建一个Message对象,里面包含MSG_ID和消息处理handler,一旦底层处理完,会根据MSG_ID将处理完结果回送给该消息对应的消息队列handler来处理。
图4.8 tryChangePin函数改变pin的状态
图4.13 showPinDialog()会回调setDialogValues()
tryChangeIccLockState改变pin状态为MSG_ENABLE_ICC_PIN_COMPLETE:开启PIN;
tryChangePin函数改变pin的状态为:MSG_CHANGE_ICC_PIN_COMPLETE:修改PIN;
showPinDialog()会回调setDialogValues()。
总结:其实无论是case那个状态最后都会执行showPinDialog()函数,然后会重新执行setDialogValues()。
5、 PIN码条件,4-8个字符。
图4.14 PIN码长度条件
图4.15 PIN码长度条
第5章 SIM卡锁定PIN解锁流程
1、插卡后,在卡初始化过程中,UiccController会从底层获取card状态,会知道要不要进行PIN校验,如果开启就暂停卡初始化流程,并弹出PIN输入框,输入正确的PIN之后,继续从卡中取信息,完成后续流程。
Sim锁定之后,开机会调用一个类,显示“请输入pin”的界面,输入pin密码之后,点击ok,调用checkPin();
通过启动一个线程CheckSimPin来调用TelephonyManager的supplyPinResultForSubscriber()接口;
并注册一个类似于Callback的虚函数onSimLockChangedResponse()并实现,这样当supplyPIN()调用返回时,触发该Callback函数。
KeyguardSimPinView.java类里面查看:
图5.1调用supplyPinReportResultForSubscriber()
首先创建一个线程并启动来维护一个Handler用于接收RIL传来的(SUPPLY_PIN_COMPLETE)消息。checkSimPin是IccCard类型。随后调用IccCard的supplyPin()方法并将Handler注册上去,此后一直wait,直到Hander收到指定消息后将其唤醒返回,并将操作结果传给其调用者。如果成功,返回值为true,代表PIN码验证成功。
unlockSim(IccCard …)里面的参数是IccCard类型的,下一步我们要去IccCard看。
图5.2 supplyPinReportResultForSubscriber()调用unlockSim()
图5.3 UnclockSim()用的是IccCard的参数
图5.4
图5.5调用UiccApplication的supplyPin()
图5.6将Handler注册上去
图5.7将handleMessage处理,调用parsePinPukErrorResult()
parsePinPukErrorResult()函数:
图5.8返回一个attemptsRemaining,即result
接着我们看一下result:
图5.9如果成功,返回值为true,代表PIN码验证成功。
此时KeyguardSimPinView中的onSimLockChangedResponse()方法会被调用,它会通知KeyguardUpdateMonitor去更新SimState的状态为IccCard.State.READY,并通知锁屏程序解锁成功。
如果失败,KeyguardSimPinView界面会一直存在,直到连续输入三次失败后,IccCard.java会发出一个Action为”ACTION_SIM_STATE_CHANGED_LOCKED”的广播,由于KeyguardUpdateMonitor对它进行了监听,所以它的onReceive()会被触发,并根据Intent携带的Extras进行SIMState的修改,此时由于reason为“PUK”,故将SIMState置为PUK_ REQUIRED,并触发所有注册监听SIM状态改变的Callback。KeyguardViewMediator的onSimStateChanged()被调用,由于SIMState状态为PUK_ REQUIRED,所以屏幕Mode被设置为LockScreen此时会进入锁屏界面按道理此时需要输入PUK码进行PIN码的修改。但是由于目前的流程中没有对PUK码的请求输入进行处理,所以导致锁屏界面解锁后整个界面不可操作,具体原因可能要调查现有的锁屏程序,不过据我所知目前Android原生态的锁屏都有这个问题,即一旦PIN码锁住之后,手机使用不了,必须将SIM拔出在支持PUK解锁的手机上解锁之后才可以继续使用。
第6章 双卡加载流程学习
1、 PhoneFactory中的makeDefaultPhone方法。
单卡还是双卡:
图6.1
TelephonyNetWorkFactory是创建网络代理的工厂;
图6.2
初始化网络模式和RIL,支持几张卡就初始化几个RIL。
图6.3
接着就是大致把流程梳理一下、其实这些都是上几次说过的内容,这次只不过把他们合并在一起、并稍微添加了一些内容。
1、开机后PhoneFactory创建出了UiccController,该对象将作为RIL的观察者,监听卡状态变化的事件。
2、当PhoneFactory创建出GsmCdmaPhone后,GsmCdmaPhone在其初始化函数中将创建出IccCardProxy对象。
IccCardProxy对象将作为UiccController的观察者,同样监听卡状态的变化。
3、当用户进行插卡操作且底层检卡成功后,modem将主动向RIL发送RIL_UNSOL_SIM_STATUS_CHANGED消息。
此时,RIL将通知它的观察者UiccController。
4、UiccController收到RIL的通知后,将调用getIccCardStatus函数主动从modem获取卡相关的信息。
一旦卡信息获取成功,UiccController将调用其onGetIccCardStatusDone函数。
5、在onGetIccCardStatusDone函数中,UiccController将判断是否创建过卡对应的UiccCard对象。
如果没有创建过,UiccController将根据卡信息创建出对应的UiccCard对象;
否则,仅进行UiccCard的更新操作。
6、当UiccCard对象被创建时,在它的构造函数中将进一步创建出对应的UiccCardApplication和IccRecords。
需要注意的是,IccRecords只是一个抽象类,
UiccCardApplication将根据modem上报的卡类型,创建出实际的子类,
即SIMRecords、RuimRecords等。
当IccRecords被创建后,同样将作为RIL的观察者,监听卡状态更新消息。
7、UiccController完成上述工作后,将通知其观察者IccCardProxy对象。
此时,卡信息还未完全加载完毕。
IccCardProxy收到UiccController的通知后,注册成为IccRecords的观察者。
8、收到modem主动上报的RIL_UNSOL_SIM_REFRESH消息后,RIL将通知IccRecords对象。
IccRecords对象收到通知后,将主动与modem通信,进一步获取卡相关的信息。
当SIMRecords收到RIL的通知后,将调用handleSimRefresh函数进行处理。
handleSimRefresh函数将负责获取或更新卡对应的文件信息。
fetchSimRecords函数,将向modem发送Request,获取卡对应的信息。
9、当卡信息加载完毕后,IccRecords将调用onAllRecordsLoaded函数,通知它的观察者。
10、IccCardProxy收到通知后,将调用broadcastInternalIccStateChangedInent函数,
发送ACTION_INTERNAL_SIM_STATE_CHANGED广播。
该广播发送后,将触发数据卡的选择流程。
1、在前文中我们提到了IccCardProxy在卡信息加载完毕后,会发送ACTION_INTERNAL_SIM_STATE_CHANGED广播。
SubscriptionInfoUpdater收到广播后,就会调用handleSimLoaded函数进行处理。
2、在handleSimLoaded函数中,主要工作将由updateSubscriptionInfoByIccId函数来承担。
updateSubscriptionInfoByIccId依次调用clearSubInfo和addSubscriptionInfoRecord函数完成实际的工作。
3、clearSubInfo主要用于清除slot Id和sub Id之间的关系。
毕竟在卡槽中重新插入一张卡后,sub Id可能发生了变化,因此需要先清理过去的缓存信息。
4、addSubscriptionInfoRecord函数调用SubscriptionController的接口进行实际工作。
SubscriptionController首先将更新一些数据库字段,
设置一些卡界面显示所需要的信息,例如SIM卡在设置界面中的名称、颜色等。
然后,SubscriptionController重新建立起Slot Id和Sub Id之间的映射关系。
最后,SubscriptionController判断终端中只有一张卡时,开始设置数据、语音和短信使用的Sub Id等。
数据卡设置完毕后,SubscriptionController将发送相应的广播信息。
5、此外,SubscriptionController调用setDefaultDataSubId设置数据卡后,
还需要利用ProxyController的接口设置每个Phone对应的无线能力,即设置每张卡使用的协议栈。
第7章 短信发送过程部分内容
1、短信应用包路径为packages/apps/Mms,查看配置文件AndroidManifest.xml文件,可以找到MMs应用的入口,就是ConversationList.java。
图7.1查看短信的入口
2、在默认的Android虚拟设备启动时,进入ConversationList.java显示短信的会话列表界面,不会有任何短信会话。
在ConversationList.java显示的短信会话列表界面,点击屏幕左下角的“新建短信”,进入短信编辑界面。此按钮的响应事件是ConversationList类中的createNewMessage(),他最主要的作用就是启动ComposeMessageActivity类的Activity。
图7.2启动ComposeMessageActivity类
图7.31发送短信调用confirmSendMessageIfNeeded()
confirmSendMessageIfNeeded()方法:
图7.32获取isMms,调用sendMsimMessage()、sendMessage()
sendMsimmessage()方法:
图7.33 sendMsimmessage()调用sendMessage()方法
confirmSendMessageIfNeeded()有两大作用:
mWorkingMessage是WorkingMessage类型,当前编辑的短信内容被抽象成了WorkingMessage类,他实现了当前编辑的短信信息涉及的所有行为和操作,其中包含:发送当前信息、为当前信息增加主题或者附件、设置当前信息的类型(短信或者彩信)、保存草稿、丢弃当前信息、设置当前信息所属的会话等。
图7.4 sendMessage方法调用mWorkingMessage.send()
接着看一下WorkingMessage的send()
……
// 准备往磁盘中写
//短信和彩信分开处理,if中处理发送彩信,else中处理发送短信
//准备发送彩信所需要的参数如:mmsUri、persister、slideshow等
//启动线程完成发送短信的操作,目的是不影响界面的返回。
sendMmsWorker(…)//调用sendMmsWorker返回继续发送短信的请求;
updateSendStates()//更新Conversation列表的界面显示。
//启动线程发送短信的操作,目的是不影响界面的返回;
sendMmsWorker(…)//调用sendMmsWorker返回继续发送短信的请求;
图7.5 WorkingMessage处理短信发送
总结:WorkingMessage类应该看作发送短信的起点,在发送短信的时候首先调用其send方法。他的if…else分别处理发送彩信、短信,如果发送短信,将当前类中的preSendSmsWorker方法继续调用sendSmsWork方法,而sendSmsWork方法中的处理逻辑主要分为两步:
图7.6 preSendSmsWorker方法调用sendSmsWork()
图7.7 sendSmsWork()创建SmsMessageSender对象sender,调用sender.sendMessage
图7.8 sender.sendMessage()调用queueMessage()
……
//循环处理多个短信接收方
……
//调用Telephony Framework层中的addMessageToUri静态方法,记录待发送的短信的内容(将需要发送的短信内容保存到数据库)
//数据库操作异常
//发送ACTION_SEND_MESSAGE广播,SmsReceiver类中的onReceiver方法会接收。
图7.9发送ACTION_SEND_MESSAGE广播
6、SmsReceiver类中的onReceiver方法接受广播。
图7.10调用onReceiveWithPrivilege()
onReceiveWithPrivilege()方法详细作用:
图7.11启动SmsReceiverService
通过AIDL启动SmsReceiverService,进入SmsReceiverService类的onStartCommand方法。
7、SmsReceiverService
进入SmsReceiverService类的onStartCommand方法。此方法通过mServiceHandler对象,在当前类中进行Handler消息的发送和接收处理。在handleMessage方法中,根据Action请求的不同类型,分为4个分支,在短信发送请求的过程中,进入ACTION_SEND_MESSAGE分支调用handleSendMessage方法,而handleSendMessage方法会继续调用sendFirstQueuedMessage方法继续传递发送短信的请求。
图12 SmsReceiverService类的onStartCommand(),执行mServiceHandler.sendMessage(msg)将消息发给自己,然后去自己的handleMessage()处理
接着会进入自己的handleMessage处理,接收到的消息是第5步发送ACTION_SEND_MESSAGE广播,然后会调用handleSendMessage()方法。
图13 handleMessage处理调用handleSendMessage()
看一下handleSendMessage(),handleSendMessage()继续调用sendFirstQueuedMessage()继续传递发送短信的请求。
图14 handleSendMessage()调用sendFirstQueuedMessage()
sendFirstQueuedMessage()方法的处理逻辑是从数据库中读取等待发送的短信的内容,根据短信内容创建SmsSingleRecipientSender对象,并调用其sendMessage方法,继续发出发送短信的请求。
//从数据库中获取要发送的短信的内容
……
//根据短信的内容创建SmsSingleRecipientSender对象
//调用他的sendMessage方法
图15数据库中获取短信内容,调用SmsSingleRecipientSender对象的sendMessage
8、SmsSingleRecipientSender类:
查看SmsSingleRecipientSender对象的sendMessage()方法:
……
//获取SmsManager对象,SmsManager位于Telephony framework层
//根据彩信内容拆分出单个或者多个彩信
//根据短信内容长短拆分出单个或者多个短信
//验证拆分出来的短信个数是否正常,以及异常处理
//通过sms类的静态方法调用,将即将发送的短信保存到发件箱中
//deliveryIntents和sentIntents两个list增加对应的Intent对象
//调用Telephony Frameworks层中的方法,继续发起短信发送请求
图16 SmsSingleRecipientSender对象的sendMessage()方法
总结:SmsSingleRecipientSender对象的sendMessage()方法
SmsManager类在TelePhony Framework层提供短信操作的相关接口供应用层调用。该类提供了短信的拆分、发送、将短信复制到SIM卡上、从SIM卡上删除短信、小区广播等操作接口。
1) 首先获取SmsManager对象并调用它的getDefault方法;其次,调用SmsManager对象的divideMessage方法完成短信的拆分、调用SmsManager对象的sendMultipartTextMessage方法完成短信的发送。
注意:是Telephony目录下的,不是Telephony gsm目录下的SmsManager。
图17 调用sendMultipartTextMessageInternal()
2) sendMultipartTextMessageInternal()方法介绍:
//判断短信的长度
//获取IccSmsInterfaceManager服务,调IccSmsInterfaceManager服务发送短信接口
//获取单个的deliveryIntent和sentIntent
图18 sendMultipartTextMessage间接调IccSmsInterfaceManager服务发送短信接口
3)IccSmsInterfaceManager服务发送短信接口:这里拿发送多条短息举例,单条短信发送与其相似。
UiccSmsController.java的sendMultipartTextForSubscriber方法,该方法会调用IccSmsInterfaceManager服务发送短信接口。
图18该方法会调用IccSmsInterfaceManager服务发送短信接口,并调用他发送短信的方法
IccSmsInterfaceManager服务发送短信接口:
图19获取短信发送接口
图20调mDispatcher的方法发送短信
mDispatcher对象是SMSDispatcher类型的,字面理解“短信分发器”,他的创建与Tracker对象的创建时机一样,均是在Phone对象创建时同时构造。
总结:
SmsManager对象接收到sendMultipartTextMessage方法调用,分别处理长短信发送和普通短信发送;最后是调用RIL对象提供的sendSMS方法,完成短信发送的RIL请求调用。
短信发送到短信中心之后,RIL会向SMSDispatcher发起EVENT_SEND_SMS_COMPLETE类型的消息通知,SMSDispatcher对象最终调用handleSendComplete方法响应短信发送的返回结果。
SMSDispatcher的sendText与sendMultipartText处理逻辑一样,可以分为两步:
1)将短信内容构造为SubmitPdu;
2)调用mCm.sendSMS方法向RIL发起短信发送请求。
SMSDispatcher短信分发器从调用sendText与sendMultipartText,到最后与RIL的交互,在这期间还会调用sendRawPdu、sendSms方法。
图21 mCi是RIL对象
mCi是RIL对象。至此短信传到了RIL层,短信请求从应用层分多个步骤传到Java Frameworks层,最后是Java Frameworks框架层拆分短信、创建PDU对象等操作,最终会调用RIL对象中发送短信的方法,将短信发送的请求转换为对应的RIL请求调用。
短信发送成功会保存对应的tracker对象到deliveryPendingList,在处理短信状态报告的同时会发出sentIntent的广播通知。短信发送失败有3次短信重发的处理机制。
图22保存对应的tracker对象到deliveryPendingList
图23调用onSent()发出sentIntent的广播通知
总结:短信发送成功决定性因素:
手机Modem将短信成功发送到短信中心;
短信中心成功发送短信到短信接收方。
在与RIL交互的时候,将RIL请求处理的结果CallBack回调handler消息对象作为参数传递给RIL对象,RIL发起短信发送的AT命令,Modem将短信发送出去并将结果告诉RIL,RIL接收到response后将使用对应的handler对象发送消息通知进行回调;
1)接着查看GsmSMSDispatcher对象中的sendSms方法,向RIL发出短信发送请求。
图24sendSms方法调用sendSmsByPstn()
sendSmsByPstn()方法中
//创建回调的handler消息对象,消息类型为EVENT_SEND_SMS_COMPLETE
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
//调用RI了对象的发送短信请求方法
mCi.sendSMS(……);
图25 tracker会保存在replay里面,传给RIL
看一下tracker
图26
2)SMSDispatcher会接收RIL对象发起的CallBack通知,在handleMessage中处理消息类型为EVEVT_SEND_SMS_COMPLETE消息。然后handleMessage会调用handlesendComplete方法处理。
总结:
1)SmsTracker对象在SMSDispatcher中创建,并传递给RIL,最后RIL对象会发出EVEVT_SEND_SMS_COMPLETE消息,该消息中仍然保存了SMSTracker对象,可见他是一直存在在SMSDispatcher与RIL对象的短信发送的请求的返回过程中。
2)通过SMSTracker对象获取sentIntent,并发出广播通知,根据sentIntent的创建过程,找到在SmsReceiver类中的onReceive方法接收此广播,然后启动SmsReceiverService服务,并在handleSmsSent方法中最终处理发送短信的返回结果。
3)短信如果发送失败,会有三次重发的机制,在SMSDispatcher类中的handleSendComplete方法中处理。