1. 短信应用启动
(a).AndroidManifest.xml配置
<activityandroid:name=".ui.BootActivity" android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
<categoryandroid:name="android.intent.category.DEFAULT" />
</intent-filter>
…...
</activity>
(b).启动时中转,我们一般会以会话形式显示短信
文件路径:com.android.mms.ui.BootActivity
public void onCreate(Bundle savedInstanceState) {
……
enterMms();
}
private void enterMms() {
……
//判断是文件夹形式显示,还是会话形式显示短信,这里启动ConversationList
if(MmsConfig.getFolderModeEnabled() && dirMode) {
intent = newIntent(this, FolderViewList.class);
intent.putExtra("floderview_key",FolderViewList.OPTION_INBOX);// show inbox by default
} else {
intent = newIntent(this, ConversationList.class);
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);///M:changed for alps00437708
startActivity(intent);
}
(c).短信会话界面的构建
文件路径:com.android.mms.ui.ConversationList
protected void onCreate(Bundle savedInstanceState) {
……
//构建界面布局
setContentView(R.layout.conversation_list_screen);
……
//初始化sim卡信息适配器,以便填充布局中ListView
initListAdapter();
}
private void initListAdapter(){
//构建会话适配器对象,其中会调用ConversationListAdapter类中bindView
mListAdapter = new ConversationListAdapter(this, null);
// ConversationList类继承ListActivity, 调用setListAdapter函数将数据进行绑定到视图
setListAdapter(mListAdapter);
……
}
文件路径:com.android.mms.ui.ConversationListAdapter
public void bindView(View view, Contextcontext, Cursor cursor) {
/每个对话记录的视图
ConversationListItem headerView =(ConversationListItem) view;
if (!mIsScrolling) {
……
//对话内容的获取
Conversation conv =Conversation.from(context, cursor);
……
//对话内容绑定到视图
headerView.bind(context, conv);
} else {
headerView.bindDefault();
}
……
}
小结:上面内容是MMS应用的启动,到会话记录界面,以及会话内容的加载过程。
2. 一条短信的发出
(a).界面(ui): 输入文本或者添加多媒体附件后,单击按钮发送后,将检查信息合法性以及区分ip或者非ip 消息,单卡或者双卡情况
文件路径:com.android.mms.ui.ComposeMessageActivity
public void onClick(View v) {
if (v == mSendButtonSms || v == mSendButtonMms || v == mSendButtonIpMessage) {
if (mSendButtonCanResponse) {
mSendButtonCanResponse = false;
//检查是否满足发送条件,包括收件人不为空,文本或多媒体内容不为空
if (isPreparedForSending()) {
……
checkRecipientsCount();
}
}
}
private void checkRecipientsCount() {
final int mmsLimitCount =MmsConfig.getMmsRecipientLimit();
//检查多媒体休息收件人个数,如果超过一定限制,会提示是否继续发送,选择继续发送时会有一些收件人被删除
if(mWorkingMessage.requiresMms() && recipientCount() > mmsLimitCount){
……
builder.setPositiveButton(R.string.yes, newDialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//这里另开启一个线程用于发送MMS消息
runOnUiThread(new Runnable() {
public void run() {
……
//单卡,双卡情况下的处理
simSelection();
}
});
}else{
//检查收件人是否为空,如果为空,则取消发送,否则进入下面else部分
……
else{
……
//单卡,双卡情况下的处理
simSelection()
}
}
}
备注:为了简化发送情况,目前只以单卡情况进行分析
private void simSelection() {
//单卡情况
if (EncapsulatedFeatureOption.MTK_GEMINI_SUPPORT == false) {
confirmSendMessageIfNeeded();
}
……
}
private voidconfirmSendMessageIfNeeded() {
if (!isRecipientsEditorVisible()) {
//首先判断联系人输入框是否可见,如果不可见,那么可以直接发送。
checkConditionsAndSendMessage(true);
return;
}
……
if(mRecipientsEditor.hasInvalidRecipient(isMms)) {
……
//检查是否有非法的联系人,如果存在,弹出AlertDialog给用户提示
} else {
//如果没有非法的联系人,那么直接发送。
checkConditionsAndSendMessage(true);
}
}
}
private voidcheckConditionsAndSendMessage(boolean bCheckEcmMode){
……
//当是ip消息时
if (mIsMessageDefaultSimIpServiceEnabled && isNetworkConnected(getApplicationContext())
&& isCurrentRecipientIpMessageUser()
&& (mIpMessageDraft != null || mIpMessageForSend != null
|| (mIpMessageDraft == null && !mWorkingMessage.requiresMms()))){
checkIpMessageBeforeSendMessage(bCheckEcmMode);
return;
}
//当是非ip消息时
final int result = mCellMgr.handleCellConn(slotId,requestType, new Runnable() {
public void run() {
……
checkIpMessageBeforeSendMessage(bCEM);
}
});
备注:当前流程只以非ip信息进行分析
private voidcheckIpMessageBeforeSendMessage(boolean bCheckEcmMode) {
……
sendMessage(bCheckEcmMode);
…..
}
private voidsendMessage(boolean bCheckEcmMode) {
……
//当前没有消息正在发送时
if (!mSendingMessage) {
……
mWorkingMessage.send(mDebugRecipients,mSelectedSimId);
……
}
}
(b).数据(data):将要发送的信息保存到数据库中,并发送广播通知
文件路径:com.android.mms.data.WorkingMessage
public voidsend(final String recipientsInUI, final int simId) {
……
//检查当前信息的mMmsState以及是否含有email地址,来判断当前信息是否会转化为彩信
if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
……
new Thread(new Runnable() {
public void run() {
final SendReq sendReq = makeSendReq(conv,subject);
slideshow.prepareForSend();
sendMmsWorker(conv, mmsUri,persister, slideshow, sendReq, simId);
updateSendStats(conv);}}, "WorkingMessage.sendMMS").start();
} else { //处理短信的逻辑
final String msgText= mText.toString();
//开启一个线程,主线程返回,在新开启的线程里会执行
new Thread(newRunnable() {
public void run(){
preSendSmsWorker(conv, msgText, recipientsInUI, simId);
updateSendStats(conv);
}
},"WorkingMessage.send SMS").start();
}
……
}
private voidpreSendSmsWorker(Conversation conv, String msgText, String recipientsInUI, intsimId) {
//首先 检查/创建 一个threadId,并且调用sendSmsWorker方法。最后删除当前会话内的草稿短信。
比较重要的是两个方法,第一个是conv.ensureThreadId,另一个是sendSmsWorker
long origThreadId = conv.getThreadId();
long threadId = conv.ensureThreadId();
……
sendSmsWorker(msgText, semiSepRecipients, threadId, simId);
……
}
private voidsendSmsWorker(String msgText, String semiSepRecipients, long threadId, intsimId) {
//获取所有收件人
String[] dests =TextUtils.split(semiSepRecipients, ";");
……
//新建一个SmsMessageSender对象,将联系人的发送地址,发送内容,会话id(threadId)作为参数,然后调用sendMessage方法
MessageSender sender = newSmsMessageSender(mActivity, dests, msgText, threadId);
sender.setSimId(simId);
try {
sender.sendMessage(threadId);
……
} catch (Exception e) {
Log.e(TAG, "Failed to send SMSmessage, threadId=" + threadId, e);
}
//回调函数,通知ComposeMessageActivity发送完成
mStatusListener.onMessageSent();
……
}
文件路径:com.android.mms.trasaction.SmsMessageSender
public booleansendMessage(long token) throws MmsException {
returnqueueMessage(token);
}
private booleanqueueMessage(long token) throws MmsException{
//检查设置,是否需要发送报告
……
//然后根据mNumberOfDests,执行Sms.addMessageToUri,将信息插入到数据库中
……
else {
EncapsulatedTelephony.Sms.addMessageToUri(mContext.getContentResolver(),
Uri.parse("content://sms/queued"), mDests[i],
mMessageText, null,mTimestamp,
true /* read */,
requestDeliveryReport,
mThreadId,
mSimId);
}
……
//发起一个广播,通知SmsReceiverService进行处理
Intent sentIt = new Intent(SmsReceiverService.ACTION_SEND_MESSAGE,null, mContext,
SmsReceiver.class);
sentIt.putExtra(EncapsulatedPhone.GEMINI_SIM_ID_KEY, mSimId);
mContext.sendBroadcast(sentIt);
}
(c). 广播中的处理逻辑
文件路径: com.android.mms.trasaction.SmsReceiverService
private finalclass ServiceHandler extends Handler {
……
public void handleMessage(Message msg){
……
else if(ACTION_SEND_MESSAGE.endsWith(action)) {
handleSendMessage(intent) ;
}
……
}
}
private voidhandleSendMessage(Intent intent) {
if(!mSending) {//当前没有信息正在发送时
if(EncapsulatedFeatureOption.MTK_GEMINI_SUPPORT) {
sendFirstQueuedMessage(intent.getIntExtra(EncapsulatedPhone.GEMINI_SIM_ID_KEY,-1));
} else {
//发送信息
sendFirstQueuedMessage(-1);
}
}
}
publicsynchronized void sendFirstQueuedMessage(int simId) {
//要发送信息保存在数据库位置
final Uri uri = Uri.parse("content://sms/queued");
……
Cursor c = SqliteWrapper.query(this, resolver, uri, SEND_PROJECTION,where, null, "date ASC");//按时间倒序查出
if (c != null) {
try {
if (c.moveToFirst()) {
String msgText =c.getString(SEND_COLUMN_BODY);
String address =c.getString(SEND_COLUMN_ADDRESS);
int threadId =c.getInt(SEND_COLUMN_THREAD_ID);
int status =c.getInt(SEND_COLUMN_STATUS);
int msgId =c.getInt(SEND_COLUMN_ID);
Uri msgUri =ContentUris.withAppendedId(Sms.CONTENT_URI, msgId);
//查出的数据封装成SmsSingleRecipientSender对象,然后进行发送
SmsMessageSendersender = new SmsSingleRecipientSender(this,
address,msgText, threadId, status == Sms.STATUS_PENDING, msgUri);
try {
sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);;
mSending = true;
}
}
}
文件路径: com.android.mms.trasaction.SmsSingleRecipientSender
public booleansendMessage(long token) throws MmsException {
……
//到这里才算得真正调用Api中接口,将短信内容细分,EncapsulatedSmsManager是MTK对SmsManager的封装
messages =EncapsulatedSmsManager.divideMessage(msgText, codingType);
……
else {
//调用EncapsulatedSmsManager中发短信的方法,EncapsulatedSmsManager是MTK对SmsManager的封装
EncapsulatedSmsManager.sendMultipartTextMessageWithEncodingType(mDest,mServiceCenter, messages,
codingType, sentIntents,deliveryIntents);
}
……
}
文件路径: com.mediatek.encapsulation.android.telephony.EncapsulatedSmsManager
public staticArrayList<String> divideMessage(String text, int encodingType) {
if (EncapsulationConstant.USE_MTK_PLATFORM){
returnSmsManagerEx.getDefault().divideMessage(text, encodingType);
} else {
returnSmsManager.getDefault().divideMessage(text);
}
}
public staticvoid sendMultipartTextMessageWithEncodingType(String destAddr, String scAddr,
ArrayList<String> parts, intencodingType, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent>deliveryIntents) {
if(EncapsulationConstant.USE_MTK_PLATFORM) {
SmsManagerEx.getDefault().sendMultipartTextMessageWithEncodingType(destAddr,scAddr,
parts, encodingType, 0,sentIntents, deliveryIntents);
} else {}
}
总结: 应用层流程简介:
上面简要介绍了短信应用的启动,以及一条短信发送到Framework层的过程。应用部分为SmsMessageSender(发送者)和SmsReceiverService(接受者),
发送者先把短信内容存入数据,再广播告知接受者,接受者短信内容从数据库查出,调用MTK封装好的api接口进行发送。
Framework层流程简介:
(Framework/opt/telephony/src/java/android/telephony/SmsManager.java)------>sendMultipartTextMessageWithEncodingType()----->GeminiSmsManager.sendMultipartTextMessageWithEncodingTypeGemini()
(mediatek/frameworks-ext/base/telephony/java/android/telephony/gemini/GeminiSmsManager.java)----->sendMultipartTextMessageWithEncodingTypeGemini()------>iccISms.sendTextWithEncodingType()
(Framework/opt/ telephony/src/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java)----->sendTextWithEncodingType()------> mIccSmsInterfaceManager.sendTextWithEncodingType()
(Framework/opt/telephony/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java)----->sendTextWithEncodingType()------>mDispatcher.sendTextWithEncodingType()
(Framework/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java)----->sendTextWithEncodingType ()------>sendRawPdu()------>sendSMS()
(Framework/opt/ telephony/src/java/com/android/internal/telephony/RIL.java)sendSMS (String smscPDU, String pdu, Message result)
RIL层流程简介:
Java-RIL将信息内容组装成RILRequest,,加入消息标志和令牌,然后将数据以byte形式写入Scoket,与C-RIL的守护进程RILD进行通讯。
RILD进程取出信息内容,根据消息标志,组装成相应AT命令,发送给基带芯片,并等待基带的处理和回应,然后逐步返回。