信息的接收工作是由底层来完成的,当有一个 新的信息时底层完成接收后会以intent的方式来通知上层应用,信息的相关内容也包含在intent当中,android所支持的信息intent都定义在android.provider.telephony.intents里面。
短信的接收
短信接收,对于上层应用程序来讲就是要处理广播事件sms_received_action,它是由frameworks发出告诉上层有新的sms已收到。在mms中,是由privilegedsmsreceiver来处理,它收到sms_received_action(android.provider.telephony.intents.sms_received_action=”android.provider.telephony.sms_received”)后会启动smsreceiverservice来做具体的处理。
smsreceiverservice会先检查短信的类型,如果是class0短信,直接在gui中显示,不做任何其他的处理,也即不会存储到数据库中,也不会在notification bar中做notification。
对于其他短信,会进行替换现有的消息,或是当作新消息插入。原则就是如果在数据库中已有的短信中,与新来的短信的原始地址和协议标识都一样,那么就把其替换成新进的短信,否则就当作新短信插入。
具体的替换流程:先用新进的短信生成一个contentvalues,再用短信的地址和协议标识当作条件到数据库中去查询,如果查到了,就替换,否则就存储。
存储的流程,也是先生成一个cotentvalues,然后取出短信的thread id和地址,地址要与联系人数据库同步一下,以保证是能识别的地址。如果thread id不是合法的,那么就用同步过的地址尝试重新生成thread id,尝试5次。然后把刷新过的thread id放到contentvalues中,把contentvalues插入到数据库中。如果设置为把信息存储到sim卡,还要调用smsmanager把信息拷贝到sim卡上。计算短信的大小,并更新至数据库。删除过期的短信,和超过数量限制的短信,然后返回插入后得到的短信uri。
最后,对于替换或插入的短信,用uri去statusbar做notification。
gui在刷新列表时也能得到新短信,因为短信已经被存储到数据库中。
彩信的接收
彩信的接收过程与短信略有不同,它主要是由应用程序负责从彩信服务中心(mmsc multimedia messaging service center)下载彩信信息。大致的流程是frameworks会先发出一条短信,告知应用程序有一个彩信,短信中含有一些信息比如过期日期,发送者手机号码,彩信的url等,然后应用程序自行通过http取回url所指的彩信内容。具体的流程为:
telephony frameworks会先发出一个intent:android.provider.telephony.intents.wap_push_received_action=”android.provider.telephony.wap_push_received”告知上层应用有一个彩信来了。这个intent中会含有一个”data”byte数组(通过byte[] data = intent.getbytearrayextra(“data”)来获取),这个byte数组是关于这个彩信的一些信息的描述,它是一个notificationind,里面含有彩信的一些信息,比如发送者手机号码,彩信的contentlocation(url)。之后是由应用程序来决定如何做下一步的处理。
在mms中是由transaction.pushreceiver.java来接收wap_push_received_action,接收到彩信通知intent后,它会做一些预处理,把data字段取出来,用pdu的工具解析成为genericpdu,然后转化为notificationind,并把它写入数据库,然后会启动transactionservice来做进一步的notification_transaction处理,同时把这个notificationind的uri也传过去。
transactionservice被唤起,在其onstartcommand中会处理一下把pushreceiver所传来的intent放入自己的messagequeue中,然后在handler.handlemessage()中处理transaction_request时处理notification_transaction。先是加载默认的一些彩信相关的配置信息,主要是mmsc,proxy和port,这些都是与运营商相关的信息,可以通过apn的设置来更改。transactionservice用pushreciver传来的notificationind的uri和加载的配置信息transactionsettings构建一个notificationtransaction对象。之后,transactionservice检查其内的二个队列,或是加入pending队列,或是直接处理(加入到正在处理队列),处理也是直接调用notificationtransaction.process()。
notificationtransaction的process()方法是继承自父类transaction的方法,它只是简单的开启一个新的线程,然后返回,这样就可以让service去处理其他的transaction request了。
在线程中,首先从downloadmanager和telephonymanager中加载一些配置信息,是否彩信设置为自动获取(auto retrieve),以及telephony是否设置为数据延迟(data_suspend),然后会采取不同的措施,再从notificationind中取出彩信的过期日期。如果配置为不取数据(更确切的说,是不现在取数据),那么就先给downloadmanager的状态标记为state_unstarted,再给mmsc发送一个notify response indication,之后结束处理,函数返回,彩信的通知处理流程到此为止。用户可以通过操作ui,用其他方法手动下载彩信,这个会在后面详细讨论。
如果设置为自动获取或者数据传输是畅通的,那么就把downloadmanager状态标记为start_downloading并开始下载彩信数据。彩信的获取是通过http到彩信的contentlocation(url)取得数据。先是调用父类方法getpdu(),传入彩信的url,最终调用httputils的httpconnection方法发送http get请求,mmsc会把彩信数据返回,作为getpdu()的返回值返回。拿到的是一个byte数组,需要用pdu的工具解析成为genericpdu,然后用pdupersister把其写入数据库,再把彩信的大小更新到数据库,到这里一个彩信的接收就算完成了。剩下的就是,因为已经获得了彩信的数据,所以要把先前的通知信息(notificationind)删除掉,然后更新一下相关的状态,给mmsc返回notify response indication,结束处理。因为数据库已经有所改变,所以ui会收到contentchanged事件,刷新ui列表,新信息就会显示出来。
如前所述,如果彩信配置设置为不自动获取,那么ui刷新了后就会显示彩信通知:到期日期,彩信大小等,并提供一个”download”按扭。用户可以点击按扭来下载彩信内容,点击按扭后,会启动transactionservice,把彩信通知的uri,和retrieve_transaction request打包进一个intent传给transactionservice。transactionservice,像处理其他的transaction一样,都是放进自己的messagequeue,然后加载默认的transactionsettings,构建retrievetransaction对象,然后处理调用retrievetransaction.process()。
retrievetransaction也是继承自transaction,其process()也是创建一个线程,然后返回。在线程中,首先它用pdu工具根据uri从数据库中加载出彩信通知(notificationind),从notificationind中取得彩信的过期日期,检查过期日期,如果彩信已经过期,那么给mmsc发送notify response indication。把downloadmanager状态标记为开始下载,然后如果彩信已过期,标记transaction状态为failed,然后返回,结束处理流程。如果一切正常,会用getpdu()从彩信的contentlocation(url)上面获取彩信内容,它会用httputils.httpconnection()通过http来获取,返回一个byte数组。用pdu工具解析byte数组,得到genericpdu,检查一下是否是新信息,是否是重复的信息,如果重复,标记状为失败,然后返回,结束处理。如果是新信息,先把genericpdu用pdupersister写入数据库中,更新信息大小和contentlocation(url)到数据库中,到这里一个彩信其实已经全部获取完了。接下来就是发送收到确认信息给mmsc,标记处理状态为成功,结束处理。这时ui应该监听到数据库变化,并刷新,新信息应该会显示给用户。
总结:与信息发送类似,数据库在接收信息过程中也扮演了重要角色,信息接收到后进行解析,然后就写入数据库,与发送不同,接收的信息没有那么多状态,一旦写入了数据库就意味着信息接收已经成功,ui也是只监听数据库的变化,一旦有变化立刻刷新显示信息。